aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornathan <thenathansmithsmith@gmail.com>2023-03-20 06:34:54 +0000
committernathan <thenathansmithsmith@gmail.com>2023-03-20 06:34:54 +0000
commitdd98918fe32b9dcdfc482a2c68481e93ceb50623 (patch)
treebf2ea0e4f4e47facde93c0cf277adb2031f7b3ec
downloadfltk_snake-main.tar.gz
fltk_snake-main.tar.bz2
fltk_snake-main.zip
first commitHEADmain
-rw-r--r--Makefile4
-rw-r--r--README.md1
-rw-r--r--images/big_bird.pngbin0 -> 121668 bytes
-rw-r--r--images/child_person.pngbin0 -> 119665 bytes
-rw-r--r--images/easter_bunny.pngbin0 -> 74939 bytes
-rw-r--r--images/friendly_frog.pngbin0 -> 157971 bytes
-rw-r--r--images/gidget.pngbin0 -> 57194 bytes
-rw-r--r--images/remy_the_rat.pngbin0 -> 127886 bytes
-rw-r--r--src/Makefile30
-rw-r--r--src/app_window.cpp26
-rw-r--r--src/app_window.h18
-rw-r--r--src/main.cpp79
-rw-r--r--src/program_data.h83
-rw-r--r--src/snake_map.cpp245
-rw-r--r--src/snake_map.h37
-rw-r--r--src/snake_utils.cpp174
-rw-r--r--src/snake_utils.h71
-rw-r--r--src/top_menu.cpp25
-rw-r--r--src/top_menu.h9
19 files changed, 802 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6a2adaf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+all:
+ @make -C src
+%:
+ @make -C src $@
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7dd0aeb
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# Snake... but written with fltk!!!
diff --git a/images/big_bird.png b/images/big_bird.png
new file mode 100644
index 0000000..6f75edc
--- /dev/null
+++ b/images/big_bird.png
Binary files differ
diff --git a/images/child_person.png b/images/child_person.png
new file mode 100644
index 0000000..329fdb4
--- /dev/null
+++ b/images/child_person.png
Binary files differ
diff --git a/images/easter_bunny.png b/images/easter_bunny.png
new file mode 100644
index 0000000..e259fea
--- /dev/null
+++ b/images/easter_bunny.png
Binary files differ
diff --git a/images/friendly_frog.png b/images/friendly_frog.png
new file mode 100644
index 0000000..483a144
--- /dev/null
+++ b/images/friendly_frog.png
Binary files differ
diff --git a/images/gidget.png b/images/gidget.png
new file mode 100644
index 0000000..427e16f
--- /dev/null
+++ b/images/gidget.png
Binary files differ
diff --git a/images/remy_the_rat.png b/images/remy_the_rat.png
new file mode 100644
index 0000000..910b866
--- /dev/null
+++ b/images/remy_the_rat.png
Binary files differ
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..148001b
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,30 @@
+FLCON = fltk-config
+COMPILER = $(shell $(FLCON) --cxx)
+CFLAGS = $(shell $(FLCON) --use-images --cxxflags)
+LDFLAGS = $(shell $(FLCON) --use-images --ldstaticflags) \
+ -static-libstdc++ -static-libgcc
+
+TARGET = ../fltk_snake
+DEH = program_data.h
+OBJS = main.o app_window.o snake_map.o top_menu.o snake_utils.o
+
+%.o: %.cpp
+ @echo compiling $<
+ @$(COMPILER) $(CFLAGS) -c -o $@ $<
+$(TARGET): $(OBJS)
+ @echo making exe
+ @$(COMPILER) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)
+debug:
+ $(COMPILER) $(CFLAGS) -c *.cpp -g
+ $(COMPILER) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) -g
+
+# Objects.
+main.o: *.cpp *.h
+app_window.o: app_window.cpp app_window.h snake_map.cpp snake_map.h $(DEH)
+snake_map.o: snake_map.cpp snake_map.h snake_utils.cpp snake_utils.h $(DEH)
+top_menu.o: top_menu.cpp top_menu.h snake_map.cpp snake_map.h $(DEH)
+snake_utils.o: snake_utils.cpp snake_utils.h $(DEH)
+
+clean:
+ rm *.o
+ rm $(TARGET)
diff --git a/src/app_window.cpp b/src/app_window.cpp
new file mode 100644
index 0000000..b38ebfc
--- /dev/null
+++ b/src/app_window.cpp
@@ -0,0 +1,26 @@
+#include "app_window.h"
+
+void AppWindow::main_init(MainData * md, int X, int Y, const char * l) {
+ mdata = md;
+
+ Fl::add_timeout(1.0 / mdata->settings.update_fps, update_cb, (void*)this);
+ Fl::add_timeout(1.0 / mdata->settings.draw_fps, draw_cb, (void*)this);
+}
+
+void AppWindow::update_cb(void * d) {
+ AppWindow * win = (AppWindow*)d;
+ MainData* mdata = win->mdata;
+
+ mdata->snake_map->update();
+
+ Fl::repeat_timeout(1.0 / win->mdata->settings.update_fps, update_cb, d);
+}
+
+void AppWindow::draw_cb(void * d) {
+ AppWindow * win = (AppWindow*)d;
+ MainData* mdata = win->mdata;
+
+ mdata->snake_map->redraw();
+
+ Fl::repeat_timeout(1.0 / win->mdata->settings.draw_fps, draw_cb, d);
+}
diff --git a/src/app_window.h b/src/app_window.h
new file mode 100644
index 0000000..87f9568
--- /dev/null
+++ b/src/app_window.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "program_data.h"
+#include "snake_map.h"
+
+class AppWindow : public Fl_Double_Window {
+ public:
+ AppWindow(MainData * md, int X, int Y, const char * l=0) : Fl_Double_Window(X, Y, l) {
+ main_init(md, X, Y, l);
+ }
+ private:
+ MainData * mdata;
+
+ static void update_cb(void * d);
+ static void draw_cb(void * d);
+
+ void main_init(MainData * md, int X, int Y, const char * l=0);
+};
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..db0e58a
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,79 @@
+#include "program_data.h"
+#include "app_window.h"
+#include "snake_map.h"
+#include "top_menu.h"
+#include "snake_utils.h"
+
+const char image_list[][NAME_MAX] = {
+ "remy_the_rat.png",
+ "friendly_frog.png",
+ "easter_bunny.png",
+ "gidget.png",
+ "child_person.png",
+ "big_bird.png"
+};
+
+void load_images(MainData * mdata) {
+ char image_path[NAME_MAX];
+ Fl_PNG_Image * new_image = NULL;
+
+ for (auto img : image_list) {
+ memset(image_path, 0, NAME_MAX);
+ snprintf(image_path, NAME_MAX, "images/%s", img);
+
+ printf("Loading %s\n", image_path);
+
+ new_image = new Fl_PNG_Image(image_path);
+
+ if (new_image->fail())
+ fprintf(stderr, "Error loading %s\n", image_path);
+
+ mdata->images.snack_images.push_back(new_image);
+ }
+
+ set_snack_images_to_tile_size(mdata);
+}
+
+int main(int argc, char ** argv) {
+ MainData mdata;
+
+ load_images(&mdata);
+
+ // Create window.
+ mdata.win = new AppWindow(
+ &mdata,
+ Fl::w() / 2,
+ Fl::h() / 2,
+ "FLTK snake"
+ );
+
+ // Snake map.
+ mdata.snake_map = new SnakeMap(&mdata);
+
+ // Top menu.
+ mdata.top_menu = new Fl_Menu_Bar(
+ 0,
+ 0,
+ mdata.win->w(),
+ mdata.settings.top_menu_height
+ );
+
+ // Length output.
+ mdata.length_output = new Fl_Output(
+ (mdata.win->w() / 2) - (mdata.settings.output_width / 2),
+ mdata.top_menu->h(),
+ mdata.settings.output_width,
+ mdata.settings.output_height,
+ "Length"
+ );
+
+ mdata.length_output->value("0");
+
+ add_menu_items(&mdata, mdata.top_menu);
+
+ mdata.win->end();
+ mdata.win->resizable(mdata.win);
+
+ mdata.win->show(argc, argv);
+ return Fl::run();
+}
diff --git a/src/program_data.h b/src/program_data.h
new file mode 100644
index 0000000..c09e309
--- /dev/null
+++ b/src/program_data.h
@@ -0,0 +1,83 @@
+#pragma once
+
+// FLTK headers.
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_PNG_Image.H>
+#include <FL/x.H>
+#include <FL/Fl_Menu_Bar.H>
+#include <FL/Fl_Menu_Item.H>
+#include <FL/Fl_Output.H>
+#include <FL/fl_ask.H>
+#include <FL/names.h>
+#include <FL/fl_draw.H>
+
+#define _USE_MATH_DEFINES
+
+// C/C++ headers.
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include <cstring>
+#include <iostream>
+#include <random>
+#include <cmath>
+#include <cstring>
+#include <ctime>
+#include <climits>
+#include <vector>
+#include <exception>
+#include <cerrno>
+
+// OS headers.
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#include <Lmcons.h>
+#define NAME_MAX MAX_PATH
+#else
+#include <unistd.h>
+#endif
+
+struct Settings {
+ // Top menu.
+ int top_menu_height = 25;
+
+ // Tiles.
+ int tile_size = 20;
+ int tile_count_w = 30;
+ int tile_count_h = 20;
+
+ // speed.
+ float update_fps = 60.0;
+ float draw_fps = 60.0;
+ int snake_speed = 2;
+
+ // Rules.
+ bool die_when_hit_self = false;
+
+ // Score output.
+ int output_width = 100;
+ int output_height = 20;
+};
+
+struct GameImages {
+ std::vector<Fl_PNG_Image*> snack_images;
+ std::vector<Fl_PNG_Image*> snack_images_tile_size;
+};
+
+struct MainData {
+ Fl_Double_Window * win = NULL;
+ class SnakeMap * snake_map = NULL;
+ Fl_Menu_Bar * top_menu = NULL;
+
+ Fl_Output * length_output = NULL;
+
+ GameImages images;
+
+ bool paused = false;
+
+ Settings settings;
+};
diff --git a/src/snake_map.cpp b/src/snake_map.cpp
new file mode 100644
index 0000000..8a104eb
--- /dev/null
+++ b/src/snake_map.cpp
@@ -0,0 +1,245 @@
+#include "snake_map.h"
+
+void SnakeMap::main_init(MainData * md) {
+ mdata = md;
+
+ end();
+ reset_all();
+}
+
+void SnakeMap::draw() {
+ fl_rectf(x(), y(), w(), h(), color());
+ fl_rect(x(), y(), w(), h(), 0x0);
+
+ draw_snake(mdata, snake_blocks);
+ draw_snack(mdata, this, snack);
+}
+
+int SnakeMap::handle(int event) {
+ switch (event) {
+ case FL_FOCUS:
+ case FL_UNFOCUS:
+ return 1;
+ case FL_KEYDOWN:
+
+ if (mdata->paused)
+ goto end_of_keydown;
+
+ switch (Fl::event_key()) {
+ case FL_Right:
+ if (direction != SNAKE_LEFT)
+ direction = SNAKE_RIGHT;
+
+ break;
+ case FL_Left:
+ if (direction != SNAKE_RIGHT)
+ direction = SNAKE_LEFT;
+
+ break;
+ case FL_Up:
+ if (direction != SNAKE_DOWN)
+ direction = SNAKE_UP;
+
+ break;
+ case FL_Down:
+ if (direction != SNAKE_UP)
+ direction = SNAKE_DOWN;
+
+ break;
+ default:
+ break;
+ }
+
+end_of_keydown: // Yes I know. goto sucks.
+ mdata->win->handle(FL_SHORTCUT); // A hack for keyboard shortcuts.
+ return 1;
+ case FL_KEYUP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+void SnakeMap::update() {
+ int i;
+ SnakeBlock block_behind;
+ SNAKE_DIR snake_dir;
+
+ if (mdata->paused)
+ return;
+
+ // Set head direction.
+ snake_blocks.front().current_direction = direction;
+
+ // Update snake blocks.
+ for (i = 0; i < snake_blocks.size(); i++) {
+
+ // Update direction.
+ snake_dir = snake_blocks[i].current_direction;
+
+ // Go to update_position if head.
+ if (i == 0)
+ goto update_position;
+
+ block_behind = snake_blocks[i - 1];
+
+ switch (block_behind.current_direction) {
+ case SNAKE_STILL:
+ break;
+ case SNAKE_RIGHT:
+ snake_dir = (snake_blocks[i].y > block_behind.y) ? SNAKE_UP : SNAKE_DOWN;
+
+ // Set to same direction.
+ if (abs(snake_blocks[i].y - block_behind.y) <= mdata->settings.snake_speed)
+ snake_dir = SNAKE_RIGHT;
+ break;
+ case SNAKE_LEFT:
+ snake_dir = (snake_blocks[i].y > block_behind.y) ? SNAKE_UP : SNAKE_DOWN;
+
+ if (abs(snake_blocks[i].y - block_behind.y) <= mdata->settings.snake_speed)
+ snake_dir = SNAKE_LEFT;
+ break;
+ case SNAKE_UP:
+ snake_dir = (snake_blocks[i].x > block_behind.x) ? SNAKE_LEFT : SNAKE_RIGHT;
+
+ if (abs(snake_blocks[i].x - block_behind.x) <= mdata->settings.snake_speed)
+ snake_dir = SNAKE_UP;
+ break;
+ case SNAKE_DOWN:
+ snake_dir = (snake_blocks[i].x > block_behind.x) ? SNAKE_LEFT : SNAKE_RIGHT;
+
+ if (abs(snake_blocks[i].x - block_behind.x) <= mdata->settings.snake_speed)
+ snake_dir = SNAKE_DOWN;
+ break;
+ default:
+ break;
+ }
+
+ // Set new direction value.
+ snake_blocks[i].current_direction = snake_dir;
+ put_snake_blocks_side_by_side(mdata->settings.tile_size, &snake_blocks[i], block_behind);
+
+update_position:
+ // Update position.
+ switch (snake_dir) {
+ case SNAKE_STILL:
+ break;
+ case SNAKE_RIGHT:
+ snake_blocks[i].x += mdata->settings.snake_speed;
+ break;
+ case SNAKE_LEFT:
+ snake_blocks[i].x -= mdata->settings.snake_speed;
+ break;
+ case SNAKE_UP:
+ snake_blocks[i].y -= mdata->settings.snake_speed;
+ break;
+ case SNAKE_DOWN:
+ snake_blocks[i].y += mdata->settings.snake_speed;
+ break;
+ default:
+ break;
+ }
+
+ // Hit self (but not the first and second block).
+ if (mdata->settings.die_when_hit_self && i > 2)
+ if (snake_blocks_hit(mdata->settings.tile_size, snake_blocks.front(), snake_blocks[i])) {
+ reset_all();
+ return;
+ }
+ }
+
+ // Check is out of bounds.
+ if (snake_block_out_of_bounds(snake_blocks.front())) {
+ reset_all();
+ return;
+ }
+
+ // Eat yummies.
+ if (snake_block_eat_snack(mdata->settings.tile_size, this, snake_blocks.front(), snack)) {
+ reset_snack();
+ add_snake_block();
+ return;
+ }
+}
+
+void SnakeMap::reset_size() {
+ w(mdata->settings.tile_size * mdata->settings.tile_count_w);
+ h(mdata->settings.tile_size * mdata->settings.tile_count_h);
+
+ // Center.
+ position(
+ (mdata->win->w() / 2) - (w() / 2),
+ (mdata->win->h() / 2) - (h() / 2)
+ );
+
+ mdata->win->redraw();
+}
+
+void SnakeMap::reset_snake() {
+ direction = SNAKE_STILL;
+ snake_blocks.clear();
+
+ // Create snake head.
+ SnakeBlock head_block;
+ head_block.part = SNAKE_HEAD;
+ head_block.current_direction = SNAKE_STILL;
+
+ // Center snake.
+ head_block.x = x() + (w() / 2);
+ head_block.y = y() + (h() / 2);
+
+ snake_blocks.push_back(head_block);
+ update_length_output();
+}
+
+void SnakeMap::reset_snack() {
+ snack.type = (SNACK_TYPE)random_range(0, SNACK_COUNT);
+ snack.x = random_range(0, mdata->settings.tile_count_w - 1);
+ snack.y = random_range(0, mdata->settings.tile_count_h - 1);
+}
+
+void SnakeMap::reset_all() {
+ reset_size();
+ reset_snake();
+ reset_snack();
+}
+
+bool SnakeMap::snake_block_out_of_bounds(SnakeBlock snake_block) {
+ int tile_size = mdata->settings.tile_size;
+
+ bool x_out = (snake_block.x < x()) | (snake_block.x + tile_size > x() + w());
+ bool y_out = (snake_block.y < y()) | (snake_block.y + tile_size > y() + h());
+
+ return x_out | y_out;
+}
+
+void SnakeMap::add_snake_block() {
+ SnakeBlock block_behind;
+ block_behind = snake_blocks.back();
+
+ if (snake_blocks.size() > 1)
+ snake_blocks.back().part = SNAKE_BODY;
+
+ // Create new block.
+ SnakeBlock new_block;
+ new_block.part = SNAKE_TAIL;
+ new_block.current_direction = block_behind.current_direction;
+
+ // Set position.
+ put_snake_blocks_side_by_side(mdata->settings.tile_size, &new_block, block_behind);
+
+ snake_blocks.push_back(new_block);
+ update_length_output();
+}
+
+void SnakeMap::update_length_output() {
+ if (mdata->length_output == NULL)
+ return;
+
+ char buf[NAME_MAX];
+ memset(buf, 0, NAME_MAX);
+
+ snprintf(buf, NAME_MAX, "%d", snake_blocks.size() - 1); // Head does not
+ // count.
+ mdata->length_output->value(buf);
+}
diff --git a/src/snake_map.h b/src/snake_map.h
new file mode 100644
index 0000000..a10c123
--- /dev/null
+++ b/src/snake_map.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "program_data.h"
+#include "snake_utils.h"
+
+class SnakeMap : public Fl_Group {
+ public:
+ SnakeMap(MainData * md) : Fl_Group(0, 0, 0, 0) {
+ main_init(md);
+ }
+
+ void draw();
+ int handle(int event);
+ void update();
+
+ // Always reset size first then snake.
+ void reset_size();
+ void reset_snake();
+ void reset_snack();
+ void reset_all();
+
+ SNAKE_DIR snake_direction() { return direction; }
+ void snake_direction(SNAKE_DIR direction) { this->direction = direction; }
+
+ void add_snake_block();
+
+ void update_length_output();
+ private:
+ MainData * mdata;
+ SNAKE_DIR direction = SNAKE_STILL;
+ SnakeSnack snack;
+
+ std::vector<SnakeBlock> snake_blocks;
+
+ void main_init(MainData * md);
+ bool snake_block_out_of_bounds(SnakeBlock snake_block);
+};
diff --git a/src/snake_utils.cpp b/src/snake_utils.cpp
new file mode 100644
index 0000000..c8cfa30
--- /dev/null
+++ b/src/snake_utils.cpp
@@ -0,0 +1,174 @@
+#include "snake_utils.h"
+
+bool snake_blocks_hit(int tile_size, SnakeBlock b1, SnakeBlock b2) {
+ return b1.x + tile_size >= b2.x
+ && b1.x <= b2.x + tile_size
+ && b1.y + tile_size >= b2.y
+ && b1.y <= b2.y + tile_size;
+}
+
+bool snake_block_eat_snack(int tile_size, const Fl_Group * snake_map, SnakeBlock snake_block, SnakeSnack snack) {
+ // Get snack x and y pixel locations.
+ int snack_x, snack_y;
+ snack_x = (snack.x * tile_size) + snake_map->x();
+ snack_y = (snack.y * tile_size) + snake_map->y();
+
+ return snake_block.x + tile_size >= snack_x
+ && snake_block.x <= snack_x + tile_size
+ && snake_block.y + tile_size >= snack_y
+ && snake_block.y <= snack_y + tile_size;
+}
+
+int put_snake_blocks_side_by_side(int tile_size, SnakeBlock * b1, SnakeBlock b2) {
+ if (b1 == NULL)
+ return -1;
+ if (b1->current_direction != b2.current_direction)
+ return -1;
+
+ switch (b1->current_direction) {
+ case SNAKE_STILL:
+ break;
+ case SNAKE_RIGHT:
+ b1->x = b2.x - tile_size;
+ b1->y = b2.y;
+ break;
+ case SNAKE_LEFT:
+ b1->x = b2.x + tile_size;
+ b1->y = b2.y;
+ break;
+ case SNAKE_UP:
+ b1->x = b2.x;
+ b1->y = b2.y + tile_size;
+ break;
+ case SNAKE_DOWN:
+ b1->x = b2.x;
+ b1->y = b2.y - tile_size;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int random_range(int x, int y) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<> distr(x, y);
+
+ return distr(gen);
+}
+
+void draw_snake_block(MainData * mdata, SnakeBlock snake_block) {
+ Fl_Color draw_color;
+
+ switch (snake_block.part) {
+ case SNAKE_HEAD:
+ draw_color = FL_BLACK;
+ break;
+ case SNAKE_BODY:
+ draw_color = FL_YELLOW;
+ break;
+ case SNAKE_TAIL:
+ draw_color = FL_BLUE;
+ break;
+ default:
+ break;
+ }
+
+ fl_rectf(snake_block.x, snake_block.y, mdata->settings.tile_size, mdata->settings.tile_size, draw_color);
+}
+
+void draw_snake(MainData * mdata, std::vector<SnakeBlock> snake_blocks) {
+ for (auto b : snake_blocks)
+ draw_snake_block(mdata, b);
+}
+
+void draw_snack(MainData * mdata, const Fl_Group * snake_map, SnakeSnack snack) {
+ Fl_PNG_Image * draw_image;
+ int draw_x, draw_y;
+
+ draw_image = mdata->images.snack_images_tile_size[snack.type];
+ draw_x = (snack.x * mdata->settings.tile_size) + snake_map->x();
+ draw_y = (snack.y * mdata->settings.tile_size) + snake_map->y();
+
+ if (draw_image->fail()) {
+ fl_rectf(
+ draw_x,
+ draw_y,
+ mdata->settings.tile_size,
+ mdata->settings.tile_size,
+ 0x0
+ );
+
+ return;
+ }
+
+ draw_image->draw(draw_x, draw_y);
+}
+
+int str_to_int(const char * str, size_t n, int * int_value) {
+ int i;
+ int cvalue;
+ int value_muliplier = 1;
+ int res_value = 0;
+ int neg = 1; // -1 for negative and 1 for whole.
+ size_t str_len; // String length.
+ int end_at = 0; // Where loop should end.
+
+ if (str == NULL || int_value == NULL || n <= 0)
+ return -1;
+
+ // Get string length
+ str_len = strnlen(str, n);
+
+ if (str_len <= 0)
+ return -1;
+
+ // Is negative.
+ if (str[0] == '-') {
+ neg = -1;
+ end_at = 1; // If negative 0 item in 'str' is skipped.
+ }
+
+ // Do the math.
+ for (i = str_len - 1; i >= end_at; i--) {
+ cvalue = char_to_int(str[i]);
+
+ // Character not a number.
+ if (cvalue == -1)
+ return -1;
+
+ // Do the same math that is down below.
+ res_value += cvalue * value_muliplier;
+ value_muliplier *= 10;
+ }
+
+ /*
+ * "436"
+ * res_value = (6 * 1) + (3 * 10) + (4 * 100)
+ */
+
+ *int_value = (res_value * neg);
+ return 0;
+}
+
+int char_to_int(char c) {
+ int cvalue = (int)c;
+
+ // Not a number.
+ // 48 to 57 is 0 to 9 in ascii.
+ if (cvalue < 48 || cvalue > 57)
+ return -1;
+
+ return cvalue - 48; // 48 is the value of zero in ascii.
+}
+
+void set_snack_images_to_tile_size(MainData * mdata) {
+ mdata->images.snack_images_tile_size.clear();
+
+ for (auto img : mdata->images.snack_images)
+ mdata->images.snack_images_tile_size.push_back(
+ (Fl_PNG_Image*)img->copy(mdata->settings.tile_size, mdata->settings.tile_size)
+ );
+}
diff --git a/src/snake_utils.h b/src/snake_utils.h
new file mode 100644
index 0000000..d48dbf3
--- /dev/null
+++ b/src/snake_utils.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include "program_data.h"
+
+enum SNAKE_DIRECTIONS {
+ SNAKE_STILL,
+ SNAKE_RIGHT,
+ SNAKE_LEFT,
+ SNAKE_UP,
+ SNAKE_DOWN
+};
+
+typedef unsigned char SNAKE_DIR;
+
+enum SNAKE_PARTS {
+ SNAKE_HEAD,
+ SNAKE_BODY,
+ SNAKE_TAIL
+};
+
+typedef unsigned char SNAKE_PART;
+
+struct SnakeBlock {
+ SNAKE_PART part;
+ SNAKE_DIR current_direction;
+ int x, y;
+};
+
+// Please dont sue. Its open source software.
+enum SNACKS_TYPES {
+ REMY_THE_RAT = 0, // From a movie about a rat that cooks which is kind of gross
+ // when you think about it.
+ FRIENDLY_FROG = 1, // Something I made up.
+ EASTER_BUNNY = 2, // Hopefully not copyrighted. If so my childhood was a lie
+ // )---:
+ GIDGET_THE_POMERANIAN = 3, // A dogo in a movie made for a generation of childen
+ // that cant sit through an entire movie.
+ CHILD_PERSON = 4, // What the hell is a child person?
+ BIG_BIRD = 5 // A big fat bird that hants peoples nightmears.
+};
+
+#define SNACK_COUNT 5
+
+// You can put your lawers away now, Chairen (I most like messspeld 'Chairen'
+// but thats ok because I am a Utard).
+
+typedef unsigned char SNACK_TYPE;
+
+struct SnakeSnack {
+ SNACK_TYPE type;
+ int x, y; // Tile position on snake map.
+};
+
+bool snake_blocks_hit(int tile_size, SnakeBlock b1, SnakeBlock b2);
+bool snake_block_eat_snack(int tile_size, const Fl_Group * snake_map, SnakeBlock snake_block, SnakeSnack snack);
+
+int put_snake_blocks_side_by_side(int tile_size, SnakeBlock * b1, SnakeBlock b2);
+
+int random_range(int x, int y);
+
+// Drawing.
+void draw_snake_block(MainData * mdata, SnakeBlock snake_block);
+void draw_snake(MainData * mdata, std::vector<SnakeBlock> snake_blocks);
+
+// 'Fl_Group * snake_map' is just the SnakeMap.
+void draw_snack(MainData * mdata, const Fl_Group * snake_map, SnakeSnack snack);
+
+int str_to_int(const char * str, size_t n, int * int_value);
+int char_to_int(char c);
+
+void set_snack_images_to_tile_size(MainData * mdata);
diff --git a/src/top_menu.cpp b/src/top_menu.cpp
new file mode 100644
index 0000000..97b9861
--- /dev/null
+++ b/src/top_menu.cpp
@@ -0,0 +1,25 @@
+#include "top_menu.h"
+
+
+void new_game_cb(Fl_Widget * w, void * d) {
+ MainData * mdata = (MainData*)d;
+
+ mdata->snake_map->reset_all();
+}
+
+void pause_cb(Fl_Widget * w, void * d) {
+ MainData * mdata = (MainData*)d;
+
+ // Get button.
+ Fl_Menu_Bar * menu_bar = (Fl_Menu_Bar*)w;
+ const Fl_Menu_Item * button = menu_bar->mvalue();
+
+ // Set paused.
+ mdata->paused = (bool)button->value();
+}
+
+void add_menu_items(MainData * mdata, Fl_Menu_Bar * top_menu) {
+ // Game.
+ top_menu->add("&game/New game", FL_CTRL + 'n', (Fl_Callback*)new_game_cb, (void*)mdata);
+ top_menu->add("&game/pause", 'p', (Fl_Callback*)pause_cb, (void*)mdata, FL_MENU_TOGGLE | (mdata->paused ? FL_MENU_VALUE : 0x0));
+}
diff --git a/src/top_menu.h b/src/top_menu.h
new file mode 100644
index 0000000..2919347
--- /dev/null
+++ b/src/top_menu.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "program_data.h"
+#include "snake_map.h"
+
+void new_game_cb(Fl_Widget * w, void * d);
+void pause_cb(Fl_Widget * w, void * d);
+
+void add_menu_items(MainData * mdata, Fl_Menu_Bar * top_menu);