mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 03:36:51 +01:00 
			
		
		
		
	Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9e353443c2 | ||
|  | d02f3b4acb | ||
|  | f2b1c81279 | ||
|  | 768617a129 | ||
|  | 8dfd009e0e | ||
|  | 321f694bb8 | ||
|  | 4514ecd839 | ||
|  | 5e67571908 | ||
|  | c293fbe0c7 | ||
|  | 93fb84206d | ||
|  | 35aa6922d6 | ||
|  | 7abf6388f8 | ||
|  | 7aeb1a0aac | ||
|  | 60bdcf0ba5 | ||
|  | a623976a0e | ||
|  | 556a522637 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -18,3 +18,5 @@ build/*.o | ||||
| build/*.d | ||||
| apidoc/python/build | ||||
| *.vim | ||||
| *.tox | ||||
| *.nvim* | ||||
|   | ||||
| @@ -55,9 +55,9 @@ author = 'Jakob Kreuze' | ||||
| # built documents. | ||||
| # | ||||
| # The short X.Y version. | ||||
| version = '0.11.0' | ||||
| version = '0.11.1' | ||||
| # The full version, including alpha/beta/rc tags. | ||||
| release = '0.11.0' | ||||
| release = '0.11.1' | ||||
|  | ||||
| # The language for content autogenerated by Sphinx. Refer to documentation | ||||
| # for a list of supported languages. | ||||
| @@ -151,7 +151,4 @@ texinfo_documents = [ | ||||
|     (master_doc, 'toxic_api', 'toxic_api Documentation', | ||||
|      author, 'toxic_api', 'One line description of project.', | ||||
|      'Miscellaneous'), | ||||
| ] | ||||
|  | ||||
|  | ||||
|  | ||||
| ] | ||||
| @@ -19,6 +19,8 @@ ifneq ($(AUDIO), disabled) | ||||
| ifneq ($(VIDEO), disabled) | ||||
|     -include $(CHECKS_DIR)/video.mk | ||||
| endif | ||||
| endif | ||||
| endif | ||||
|  | ||||
| #check if we want to build with game support | ||||
| GAMES := $(shell if [ -z "$(DISABLE_GAMES)" ] || [ "$(DISABLE_GAMES)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| @@ -26,9 +28,6 @@ ifneq ($(GAMES), disabled) | ||||
|     -include $(CHECKS_DIR)/games.mk | ||||
| endif | ||||
|  | ||||
| endif | ||||
| endif | ||||
|  | ||||
| # Check if we want build sound notifications support | ||||
| SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(SND_NOTIFY), disabled) | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Variables for game support | ||||
| GAMES_CFLAGS = -DGAMES | ||||
| GAMES_OBJ = game_base.o game_centipede.o game_chess.o game_util.o game_snake.o | ||||
| GAMES_OBJ = game_base.o game_centipede.o game_chess.o game_life.o game_util.o game_snake.o | ||||
| CFLAGS += $(GAMES_CFLAGS) | ||||
| OBJ += $(GAMES_OBJ) | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Version | ||||
| TOXIC_VERSION = 0.11.0 | ||||
| TOXIC_VERSION = 0.11.1 | ||||
| REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") | ||||
| ifneq (, $(findstring error, $(REV))) | ||||
|     VERSION = $(TOXIC_VERSION) | ||||
|   | ||||
| @@ -166,4 +166,4 @@ JFreegman <JFreegman@gmail\&.com> | ||||
| .sp | ||||
| Project page: https://github\&.com/JFreegman/toxic | ||||
| .sp | ||||
| IRC channel: chat\&.freenode\&.net#tox | ||||
| IRC channel: irc\&.libera\&.chat#tox | ||||
|   | ||||
| @@ -105,4 +105,4 @@ LINKS | ||||
| ----- | ||||
| Project page: <https://github.com/JFreegman/toxic> | ||||
|  | ||||
| IRC channel: chat.freenode.net#tox | ||||
| IRC channel: irc.libera.chat#tox | ||||
|   | ||||
| @@ -2,12 +2,12 @@ | ||||
| .\"     Title: toxic.conf | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2020-11-18 | ||||
| .\"      Date: 2020-12-05 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC\&.CONF" "5" "2020\-11\-18" "toxic __VERSION__" "Toxic Manual" | ||||
| .TH "TOXIC\&.CONF" "5" "2020\-12\-05" "toxic __VERSION__" "Toxic Manual" | ||||
| .\" ----------------------------------------------------------------- | ||||
| .\" * Define some portability stuff | ||||
| .\" ----------------------------------------------------------------- | ||||
| @@ -414,7 +414,7 @@ Configuration example\&. | ||||
| .sp | ||||
| Project page: https://github\&.com/JFreegman/toxic | ||||
| .sp | ||||
| IRC channel: chat\&.freenode\&.net#tox | ||||
| IRC channel: irc\&.libera\&.chat#tox | ||||
| .SH "AUTHORS" | ||||
| .sp | ||||
| JFreegman <JFreegman@gmail\&.com> | ||||
|   | ||||
| @@ -267,7 +267,7 @@ RESOURCES | ||||
| --------- | ||||
| Project page: <https://github.com/JFreegman/toxic> | ||||
|  | ||||
| IRC channel:  chat.freenode.net#tox | ||||
| IRC channel:  irc.libera.chat#tox | ||||
|  | ||||
|  | ||||
| AUTHORS | ||||
|   | ||||
| @@ -131,11 +131,15 @@ mkdir -p "$BUILD_DIR" | ||||
| # Build Toxcore | ||||
| cd "$BUILD_DIR" | ||||
|  | ||||
| TOXCORE_VERSION="0.2.12" | ||||
| TOXCORE_HASH="30ae3263c9b68d3bef06f799ba9d7a67e3fad447030625f0ffa4bb22684228b0" | ||||
| # The git hash of the c-toxcore version we're using | ||||
| TOXCORE_VERSION="25a56c354937e9c8c4c50a64c3b4cfc099c34e29" | ||||
|  | ||||
| # The sha256sum of the c-toxcore tarball for TOXCORE_VERSION | ||||
| TOXCORE_HASH="8448752e6286c747130254571fde2db8e2fc073a8116f9fff489ed53af546c0a" | ||||
|  | ||||
| TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz" | ||||
|  | ||||
| wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/v$TOXCORE_VERSION.tar.gz" | ||||
| wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/$TOXCORE_VERSION.tar.gz" | ||||
| check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME" | ||||
| tar -o -xf "$TOXCORE_FILENAME" | ||||
| rm "$TOXCORE_FILENAME" | ||||
| @@ -160,8 +164,8 @@ cmake --build _build --target install | ||||
| # location with SSL_CERT_FILE env variable. | ||||
| cd "$BUILD_DIR" | ||||
|  | ||||
| CURL_VERSION="7.74.0" | ||||
| CURL_HASH="e56b3921eeb7a2951959c02db0912b5fcd5fdba5aca071da819e1accf338bbd7" | ||||
| CURL_VERSION="7.77.0" | ||||
| CURL_HASH="b0a3428acb60fa59044c4d0baae4e4fc09ae9af1d8a3aa84b2e3fbcd99841f77" | ||||
| CURL_FILENAME="curl-$CURL_VERSION.tar.gz" | ||||
|  | ||||
| wget --timeout=10 -O "$CURL_FILENAME" "https://curl.haxx.se/download/$CURL_FILENAME" | ||||
| @@ -178,7 +182,8 @@ cd curl* | ||||
|   --without-ca-path \ | ||||
|   --with-ca-fallback \ | ||||
|   --with-nghttp2 \ | ||||
|   --with-brotli | ||||
|   --with-brotli \ | ||||
|   --with-openssl | ||||
| make | ||||
| make install | ||||
| sed -i 's|-lbrotlidec |-lbrotlidec-static -lbrotlicommon-static |g' $BUILD_DIR/prefix-curl/lib/pkgconfig/libcurl.pc | ||||
| @@ -214,6 +219,7 @@ CFLAGS="-static" PKG_CONFIG_PATH="$BUILD_DIR/prefix-toxcore/lib64/pkgconfig:$BUI | ||||
|   ENABLE_PYTHON=0 \ | ||||
|   ENABLE_RELEASE=1 \ | ||||
|   ENABLE_ASAN=0 \ | ||||
|   DISABLE_GAMES=0 \ | ||||
|   install | ||||
|  | ||||
|  | ||||
| @@ -295,3 +301,4 @@ mv "$PREPARE_ARTIFACT_DIR" "$PREPARE_ARTIFACT_DIR/../$ARTIFACT_NAME" | ||||
| tar -cJf "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_NAME" | ||||
| mv "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_DIR" | ||||
| chmod 777 -R "$ARTIFACT_DIR" | ||||
|  | ||||
|   | ||||
| @@ -82,6 +82,7 @@ static const char *chat_cmd_list[] = { | ||||
|     "/conference", | ||||
| #ifdef GAMES | ||||
|     "/game", | ||||
|     "/play", | ||||
| #endif | ||||
|     "/help", | ||||
|     "/invite", | ||||
| @@ -94,7 +95,6 @@ static const char *chat_cmd_list[] = { | ||||
|     "/nick", | ||||
|     "/note", | ||||
|     "/nospam", | ||||
|     "/play", | ||||
|     "/quit", | ||||
|     "/savefile", | ||||
|     "/sendfile", | ||||
| @@ -771,7 +771,7 @@ void chat_onGameInvite(ToxWindow *self, Tox *m, uint32_t friend_number, const ui | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (length < GAME_PACKET_HEADER_SIZE) { | ||||
|     if (length < GAME_PACKET_HEADER_SIZE || length > GAME_MAX_DATA_SIZE) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -179,12 +179,6 @@ void cmd_game_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     bool force_small = false; | ||||
|  | ||||
|     if (argc >= 2) { | ||||
|         force_small = strcasecmp(argv[2], "small") == 0; | ||||
|     } | ||||
|  | ||||
|     if (!Friends.list[self->num].game_invite.pending) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending game invite."); | ||||
|         return; | ||||
| @@ -200,7 +194,7 @@ void cmd_game_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg | ||||
|     uint8_t *data = Friends.list[self->num].game_invite.data; | ||||
|     size_t length = Friends.list[self->num].game_invite.data_length; | ||||
|  | ||||
|     int ret = game_initialize(self, m, type, id, data, length, force_small); | ||||
|     int ret = game_initialize(self, m, type, id, data, length); | ||||
|  | ||||
|     switch (ret) { | ||||
|         case 0: { | ||||
|   | ||||
| @@ -88,7 +88,7 @@ static const char *conference_cmd_list[] = { | ||||
|     "/decline", | ||||
|     "/exit", | ||||
|     "/conference", | ||||
| #ifdef GAME | ||||
| #ifdef GAMES | ||||
|     "/game", | ||||
| #endif | ||||
|     "/help", | ||||
|   | ||||
							
								
								
									
										210
									
								
								src/game_base.c
									
									
									
									
									
								
							
							
						
						
									
										210
									
								
								src/game_base.c
									
									
									
									
									
								
							| @@ -29,12 +29,16 @@ | ||||
| #include "game_centipede.h" | ||||
| #include "game_base.h" | ||||
| #include "game_chess.h" | ||||
| #include "game_life.h" | ||||
| #include "game_snake.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "settings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern struct Winthread Winthread; | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| /* | ||||
|  * Determines the base rate at which game objects should update their state. | ||||
| @@ -51,20 +55,20 @@ extern struct Winthread Winthread; | ||||
|  | ||||
|  | ||||
| /* Determines if window is large enough for a respective window type */ | ||||
| #define WINDOW_SIZE_LARGE_SQUARE_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_SQUARE_Y))\ | ||||
|                                                   && ((max_x) >= (GAME_MAX_SQUARE_X))) | ||||
| #define WINDOW_SIZE_SQUARE_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_SQUARE_Y_DEFAULT))\ | ||||
|                                              && ((max_x) >= (GAME_MAX_SQUARE_X_DEFAULT))) | ||||
|  | ||||
| #define WINDOW_SIZE_SMALL_SQUARE_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_SQUARE_Y_SMALL))\ | ||||
|                                                   && ((max_x) >= (GAME_MAX_SQUARE_X_SMALL))) | ||||
| #define WINDOW_SIZE_LARGE_SQUARE_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_SQUARE_Y_LARGE))\ | ||||
|                                                    && ((max_x) >= (GAME_MAX_SQUARE_X_LARGE))) | ||||
|  | ||||
| #define WINDOW_SIZE_LARGE_RECT_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_RECT_Y))\ | ||||
|                                                   && ((max_x) >= (GAME_MAX_RECT_X))) | ||||
| #define WINDOW_SIZE_RECT_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_RECT_Y_DEFAULT))\ | ||||
|                                            && ((max_x) >= (GAME_MAX_RECT_X_DEFAULT))) | ||||
|  | ||||
| #define WINDOW_SIZE_SMALL_RECT_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_RECT_Y_SMALL))\ | ||||
|                                                   && ((max_x) >= (GAME_MAX_RECT_X_SMALL))) | ||||
| #define WINDOW_SIZE_LARGE_RECT_VALID(max_x, max_y)((((max_y) - 4) >= (GAME_MAX_RECT_Y_LARGE))\ | ||||
|                                                  && ((max_x) >= (GAME_MAX_RECT_X_LARGE))) | ||||
|  | ||||
|  | ||||
| static ToxWindow *game_new_window(GameType type, uint32_t friendnumber); | ||||
| static ToxWindow *game_new_window(Tox *m, GameType type, uint32_t friendnumber); | ||||
|  | ||||
| struct GameList { | ||||
|     const char *name; | ||||
| @@ -74,6 +78,7 @@ struct GameList { | ||||
| static struct GameList game_list[] = { | ||||
|     { "centipede", GT_Centipede  }, | ||||
|     { "chess",     GT_Chess      }, | ||||
|     { "life",      GT_Life       }, | ||||
|     { "snake",     GT_Snake      }, | ||||
|     {  NULL,       GT_Invalid    }, | ||||
| }; | ||||
| @@ -123,6 +128,28 @@ bool game_type_is_multiplayer(GameType type) | ||||
|     return type == GT_Chess; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Sends a notification to the window associated with `game`. | ||||
|  * | ||||
|  * `message` - the notification message that will be displayed. | ||||
|  */ | ||||
| void game_window_notify(const GameData *game, const char *message) | ||||
| { | ||||
|     ToxWindow *self = get_window_ptr(game->window_id); | ||||
|  | ||||
|     if (self == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (self->active_box != -1) { | ||||
|         box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message, | ||||
|                     self->active_box, "%s", message); | ||||
|     } else { | ||||
|         box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message, | ||||
|                    &self->active_box, self->name, "%s", message); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Returns the current wall time in milliseconds */ | ||||
| TIME_MS get_time_millis(void) | ||||
| { | ||||
| @@ -135,13 +162,17 @@ void game_kill(ToxWindow *self) | ||||
| { | ||||
|     GameData *game = self->game; | ||||
|  | ||||
|     if (game->cb_game_kill) { | ||||
|         game->cb_game_kill(game, game->cb_game_kill_data); | ||||
|     if (game) { | ||||
|         if (game->cb_game_kill) { | ||||
|             game->cb_game_kill(game, game->cb_game_kill_data); | ||||
|         } | ||||
|  | ||||
|         delwin(game->window); | ||||
|         free(game->messages); | ||||
|         free(game); | ||||
|     } | ||||
|  | ||||
|     delwin(game->window); | ||||
|     free(game->messages); | ||||
|     free(game); | ||||
|     kill_notifs(self->active_box); | ||||
|     del_window(self); | ||||
| } | ||||
|  | ||||
| @@ -188,6 +219,11 @@ static int game_initialize_type(GameData *game, const uint8_t *data, size_t leng | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case GT_Life: { | ||||
|             ret = life_initialize(game); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         default: { | ||||
|             break; | ||||
|         } | ||||
| @@ -197,7 +233,7 @@ static int game_initialize_type(GameData *game, const uint8_t *data, size_t leng | ||||
| } | ||||
|  | ||||
| int game_initialize(const ToxWindow *parent, Tox *m, GameType type, uint32_t id, const uint8_t *multiplayer_data, | ||||
|                     size_t length, bool force_small_window) | ||||
|                     size_t length) | ||||
| { | ||||
|     int max_x; | ||||
|     int max_y; | ||||
| @@ -205,23 +241,7 @@ int game_initialize(const ToxWindow *parent, Tox *m, GameType type, uint32_t id, | ||||
|  | ||||
|     max_y -= (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT); | ||||
|  | ||||
|     int max_game_window_x = GAME_MAX_SQUARE_X; | ||||
|     int max_game_window_y = GAME_MAX_SQUARE_Y; | ||||
|  | ||||
|     if (!force_small_window && !WINDOW_SIZE_LARGE_SQUARE_VALID(max_x, max_y)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (force_small_window) { | ||||
|         max_game_window_x = GAME_MAX_SQUARE_X_SMALL; | ||||
|         max_game_window_y = GAME_MAX_SQUARE_Y_SMALL; | ||||
|  | ||||
|         if (!WINDOW_SIZE_SMALL_SQUARE_VALID(max_x, max_y)) { | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ToxWindow *self = game_new_window(type, parent->num); | ||||
|     ToxWindow *self = game_new_window(m, type, parent->num); | ||||
|  | ||||
|     if (self == NULL) { | ||||
|         return -4; | ||||
| @@ -255,8 +275,6 @@ int game_initialize(const ToxWindow *parent, Tox *m, GameType type, uint32_t id, | ||||
|  | ||||
|     game->tox = m; | ||||
|     game->window_shape = GW_ShapeSquare; | ||||
|     game->game_max_x = max_game_window_x; | ||||
|     game->game_max_y = max_game_window_y; | ||||
|     game->parent_max_x = max_x; | ||||
|     game->parent_max_y = max_y; | ||||
|     game->update_interval = GAME_DEFAULT_UPDATE_INTERVAL; | ||||
| @@ -295,28 +313,53 @@ int game_set_window_shape(GameData *game, GameWindowShape shape) | ||||
|         return -2; | ||||
|     } | ||||
|  | ||||
|     if (shape == game->window_shape) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (shape == GW_ShapeSquare) { | ||||
|         game->game_max_x = GAME_MAX_SQUARE_X; | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     const int max_x = game->parent_max_x; | ||||
|     const int max_y = game->parent_max_y; | ||||
|  | ||||
|     if (WINDOW_SIZE_LARGE_RECT_VALID(max_x, max_y)) { | ||||
|         game->game_max_x = GAME_MAX_RECT_X; | ||||
|         game->game_max_y = GAME_MAX_RECT_Y; | ||||
|         return 0; | ||||
|     } | ||||
|     switch (shape) { | ||||
|         case GW_ShapeSquare: { | ||||
|             if (WINDOW_SIZE_SQUARE_VALID(max_x, max_y)) { | ||||
|                 game->game_max_x = GAME_MAX_SQUARE_X_DEFAULT; | ||||
|                 game->game_max_y = GAME_MAX_SQUARE_Y_DEFAULT; | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|     if (WINDOW_SIZE_SMALL_RECT_VALID(max_x, max_y)) { | ||||
|         game->game_max_x = GAME_MAX_RECT_X_SMALL; | ||||
|         game->game_max_y = GAME_MAX_RECT_Y_SMALL; | ||||
|         return 0; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case GW_ShapeSquareLarge: { | ||||
|             if (WINDOW_SIZE_LARGE_SQUARE_VALID(max_x, max_y)) { | ||||
|                 game->game_max_x = GAME_MAX_SQUARE_X_LARGE; | ||||
|                 game->game_max_y = GAME_MAX_SQUARE_Y_LARGE; | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case GW_ShapeRectangle: { | ||||
|             if (WINDOW_SIZE_RECT_VALID(max_x, max_y)) { | ||||
|                 game->game_max_x = GAME_MAX_RECT_X_DEFAULT; | ||||
|                 game->game_max_y = GAME_MAX_RECT_Y_DEFAULT; | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case GW_ShapeRectangleLarge: { | ||||
|             if (WINDOW_SIZE_LARGE_RECT_VALID(max_x, max_y)) { | ||||
|                 game->game_max_x = GAME_MAX_RECT_X_LARGE; | ||||
|                 game->game_max_y = GAME_MAX_RECT_Y_LARGE; | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         default: { | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| @@ -497,20 +540,20 @@ static void game_draw_border(const GameData *game, const int max_x, const int ma | ||||
|     const int x = (max_x - game_max_x) / 2; | ||||
|     const int y = (max_y - game_max_y) / 2; | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(GAME_BORDER_COLOUR)); | ||||
|     wattron(win, COLOR_PAIR(GAME_BORDER_COLOUR)); | ||||
|  | ||||
|     mvwaddch(win, y, x, ' '); | ||||
|     mvwhline(win, y, x + 1, ' ', game_max_x - 1); | ||||
|     mvwvline(win, y + 1, x, ' ', game_max_y - 1); | ||||
|     mvwvline(win, y, x - 1, ' ', game_max_y + 1); | ||||
|     mvwaddch(win, y, x + game_max_x, ' '); | ||||
|     mvwvline(win, y + 1, x + game_max_x, ' ', game_max_y - 1); | ||||
|     mvwvline(win, y, x + game_max_x + 1, ' ', game_max_y + 1); | ||||
|     mvwaddch(win, y + game_max_y, x, ' '); | ||||
|     mvwhline(win, y + game_max_y, x + 1, ' ', game_max_x - 1); | ||||
|     mvwaddch(win, y + game_max_y, x + game_max_x, ' '); | ||||
|     mvwaddch(win, y, x, ACS_ULCORNER); | ||||
|     mvwhline(win, y, x + 1, ACS_HLINE, game_max_x - 1); | ||||
|     mvwvline(win, y + 1, x, ACS_VLINE, game_max_y - 1); | ||||
|     mvwvline(win, y, x - 1, ACS_VLINE, game_max_y + 1); | ||||
|     mvwaddch(win, y, x + game_max_x, ACS_URCORNER); | ||||
|     mvwvline(win, y + 1, x + game_max_x, ACS_VLINE, game_max_y - 1); | ||||
|     mvwvline(win, y, x + game_max_x + 1, ACS_VLINE, game_max_y + 1); | ||||
|     mvwaddch(win, y + game_max_y, x, ACS_LLCORNER); | ||||
|     mvwhline(win, y + game_max_y, x + 1, ACS_HLINE, game_max_x - 1); | ||||
|     mvwaddch(win, y + game_max_y, x + game_max_x, ACS_LRCORNER); | ||||
|  | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(GAME_BORDER_COLOUR)); | ||||
|     wattroff(win, COLOR_PAIR(GAME_BORDER_COLOUR)); | ||||
| } | ||||
|  | ||||
| static void game_draw_status(const GameData *game, const int max_x, const int max_y) | ||||
| @@ -518,7 +561,7 @@ static void game_draw_status(const GameData *game, const int max_x, const int ma | ||||
|     WINDOW *win = game->window; | ||||
|  | ||||
|     int x = ((max_x - game->game_max_x) / 2) - 1; | ||||
|     int y = ((max_y - game->game_max_y) / 2) - 1; | ||||
|     const int y = ((max_y - game->game_max_y) / 2) - 1; | ||||
|  | ||||
|     wattron(win, A_BOLD); | ||||
|  | ||||
| @@ -707,7 +750,7 @@ bool game_onKey(ToxWindow *self, Tox *m, wint_t key, bool is_printable) | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (!game->is_multiplayer && key == KEY_F(2)) { | ||||
|     if (key == KEY_F(2) && !game->is_multiplayer) { | ||||
|         game_toggle_pause(self->game); | ||||
|         return true; | ||||
|     } | ||||
| @@ -751,9 +794,10 @@ void game_onInit(ToxWindow *self, Tox *m) | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Byte 0:   Game type | ||||
|  * Byte 1-4: Game ID | ||||
|  * Byte 5-*  Game data | ||||
|  * Byte 0:   Version | ||||
|  * Byte 1:   Game type | ||||
|  * Byte 2-5: Game ID | ||||
|  * Byte 6-*  Game data | ||||
|  */ | ||||
| void game_onPacket(ToxWindow *self, Tox *m, uint32_t friendnumber, const uint8_t *data, size_t length) | ||||
| { | ||||
| @@ -774,6 +818,8 @@ void game_onPacket(ToxWindow *self, Tox *m, uint32_t friendnumber, const uint8_t | ||||
|     } | ||||
|  | ||||
|     if (data[0] != GAME_NETWORKING_VERSION) { | ||||
|         fprintf(stderr, "Game packet rejected: wrong networking version (got %d, expected %d)\n", data[0], | ||||
|                 GAME_NETWORKING_VERSION); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -798,7 +844,7 @@ void game_onPacket(ToxWindow *self, Tox *m, uint32_t friendnumber, const uint8_t | ||||
|     } | ||||
| } | ||||
|  | ||||
| static ToxWindow *game_new_window(GameType type, uint32_t friendnumber) | ||||
| static ToxWindow *game_new_window(Tox *m, GameType type, uint32_t friendnumber) | ||||
| { | ||||
|     const char *window_name = game_get_name_string(type); | ||||
|  | ||||
| @@ -827,7 +873,16 @@ static ToxWindow *game_new_window(GameType type, uint32_t friendnumber) | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     snprintf(ret->name, sizeof(ret->name), "%s", window_name); | ||||
|     ret->active_box = -1; | ||||
|  | ||||
|     if (game_type_is_multiplayer(type)) { | ||||
|         char nick[TOX_MAX_NAME_LENGTH]; | ||||
|         get_nick_truncate(m, nick, friendnumber); | ||||
|  | ||||
|         snprintf(ret->name, sizeof(ret->name), "%s (%s)", window_name, nick); | ||||
|     } else { | ||||
|         snprintf(ret->name, sizeof(ret->name), "%s", window_name); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
| @@ -945,6 +1000,11 @@ void game_update_score(GameData *game, long int points) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void game_set_score(GameData *game, long int val) | ||||
| { | ||||
|     game->score = val; | ||||
| } | ||||
|  | ||||
| long int game_get_score(const GameData *game) | ||||
| { | ||||
|     return game->score; | ||||
| @@ -1027,7 +1087,7 @@ void game_set_cb_on_packet(GameData *game, cb_game_on_packet *func, void *cb_dat | ||||
| /* | ||||
|  * Wraps `packet` in a header comprised of the custom packet type, game type and game id. | ||||
|  */ | ||||
| static int game_wrap_packet(const GameData *game, uint8_t *packet, size_t size, GamePacketType packet_type) | ||||
| static int game_packet_wrap(const GameData *game, uint8_t *packet, size_t size, GamePacketType packet_type) | ||||
| { | ||||
|     if (size < GAME_PACKET_HEADER_SIZE + 1) { | ||||
|         return -1; | ||||
| @@ -1046,7 +1106,7 @@ static int game_wrap_packet(const GameData *game, uint8_t *packet, size_t size, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int game_send_packet(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type) | ||||
| int game_packet_send(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type) | ||||
| { | ||||
|     if (length > GAME_MAX_DATA_SIZE) { | ||||
|         return -1; | ||||
| @@ -1054,7 +1114,7 @@ int game_send_packet(const GameData *game, const uint8_t *data, size_t length, G | ||||
|  | ||||
|     uint8_t packet[GAME_MAX_PACKET_SIZE]; | ||||
|  | ||||
|     if (game_wrap_packet(game, packet, sizeof(packet), packet_type) == -1) { | ||||
|     if (game_packet_wrap(game, packet, sizeof(packet), packet_type) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -1070,5 +1130,5 @@ int game_send_packet(const GameData *game, const uint8_t *data, size_t length, G | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return -0; | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -31,23 +31,24 @@ | ||||
| #include "game_util.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define GAME_BORDER_COLOUR BAR_TEXT | ||||
| #define GAME_BORDER_COLOUR BAR_SOLID | ||||
|  | ||||
| /* Max size of a default size square game window */ | ||||
| #define GAME_MAX_SQUARE_Y 26 | ||||
| #define GAME_MAX_SQUARE_X (GAME_MAX_SQUARE_Y * 2) | ||||
|  | ||||
| /* Max size of a small square game window */ | ||||
| #define GAME_MAX_SQUARE_Y_SMALL 18 | ||||
| #define GAME_MAX_SQUARE_X_SMALL (GAME_MAX_SQUARE_Y_SMALL * 2) | ||||
| /* Max size of a default square game window */ | ||||
| #define GAME_MAX_SQUARE_Y_DEFAULT 26 | ||||
| #define GAME_MAX_SQUARE_X_DEFAULT (GAME_MAX_SQUARE_Y_DEFAULT * 2) | ||||
|  | ||||
| /* Max size of a large square game window */ | ||||
| #define GAME_MAX_SQUARE_Y_LARGE 52 | ||||
| #define GAME_MAX_SQUARE_X_LARGE (GAME_MAX_SQUARE_Y_LARGE * 2) | ||||
|  | ||||
| /* Max size of a default size rectangle game window */ | ||||
| #define GAME_MAX_RECT_Y 24 | ||||
| #define GAME_MAX_RECT_X (GAME_MAX_RECT_Y * 4) | ||||
| #define GAME_MAX_RECT_Y_DEFAULT 24 | ||||
| #define GAME_MAX_RECT_X_DEFAULT (GAME_MAX_RECT_Y_DEFAULT * 4) | ||||
|  | ||||
| /* Max size of a small rectangle game window */ | ||||
| #define GAME_MAX_RECT_Y_SMALL 14 | ||||
| #define GAME_MAX_RECT_X_SMALL (GAME_MAX_RECT_Y_SMALL * 4) | ||||
| /* Max size of a large rectangle game window */ | ||||
| #define GAME_MAX_RECT_Y_LARGE 52 | ||||
| #define GAME_MAX_RECT_X_LARGE (GAME_MAX_RECT_Y_LARGE * 4) | ||||
|  | ||||
| /* Maximum length of a game message set with game_set_message() */ | ||||
| #define GAME_MAX_MESSAGE_SIZE 64 | ||||
| @@ -56,7 +57,7 @@ | ||||
| #define GAME_MESSAGE_DEFAULT_TIMEOUT 3 | ||||
|  | ||||
|  | ||||
| /***** NETWORKING DEFINES *****/ | ||||
| /***** START NETWORKING CONSTANTS *****/ | ||||
|  | ||||
| /* Header starts after custom packet type byte. Comprised of: NetworkVersion (1b) + GameType (1b) + id (4b) */ | ||||
| #define GAME_PACKET_HEADER_SIZE (1 + 1 + sizeof(uint32_t)) | ||||
| @@ -70,6 +71,9 @@ | ||||
| /* Current version of networking protocol */ | ||||
| #define GAME_NETWORKING_VERSION 0x01 | ||||
|  | ||||
| /***** END NETWORKING CONSTANTS *****/ | ||||
|  | ||||
|  | ||||
| typedef void cb_game_update_state(GameData *game, void *cb_data); | ||||
| typedef void cb_game_render_window(GameData *game, WINDOW *window, void *cb_data); | ||||
| typedef void cb_game_kill(GameData *game, void *cb_data); | ||||
| @@ -84,7 +88,9 @@ typedef enum GamePacketType { | ||||
|  | ||||
| typedef enum GameWindowShape { | ||||
|     GW_ShapeSquare = 0u, | ||||
|     GW_ShapeSquareLarge, | ||||
|     GW_ShapeRectangle, | ||||
|     GW_ShapeRectangleLarge, | ||||
|     GW_ShapeInvalid, | ||||
| } GameWindowShape; | ||||
|  | ||||
| @@ -99,6 +105,7 @@ typedef enum GameStatus { | ||||
| typedef enum GameType { | ||||
|     GT_Centipede = 0u, | ||||
|     GT_Chess, | ||||
|     GT_Life, | ||||
|     GT_Snake, | ||||
|     GT_Invalid, | ||||
| } GameType; | ||||
| @@ -209,8 +216,6 @@ void game_set_cb_on_packet(GameData *game, cb_game_on_packet *func, void *cb_dat | ||||
|  * `id` should be a unique integer to indentify the game instance. If we're being invited to a game | ||||
|  *   this identifier should be sent via the invite packet. | ||||
|  * | ||||
|  * `force_small_window` will make the game window small. | ||||
|  * | ||||
|  * if `multiplayer_data` is non-null this indicates that we accepted a game invite from a contact. | ||||
|  *   The data contains any information we need to initialize the game state. | ||||
|  * | ||||
| @@ -221,12 +226,12 @@ void game_set_cb_on_packet(GameData *game, cb_game_on_packet *func, void *cb_dat | ||||
|  * Return -4 on other failure. | ||||
|  */ | ||||
| int game_initialize(const ToxWindow *self, Tox *m, GameType type, uint32_t id, const uint8_t *multiplayer_data, | ||||
|                     size_t length, bool force_small_window); | ||||
|                     size_t length); | ||||
|  | ||||
| /* | ||||
|  * Sets game window to `shape` and attempts to adjust size for best fit. | ||||
|  * Sets game window to `shape`. | ||||
|  * | ||||
|  * This should be called in the game's initialize function. | ||||
|  * This must be called on game initialization. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 if window is too small or shape is invalid. | ||||
| @@ -285,11 +290,23 @@ void game_show_high_score(GameData *game, bool show_high_score); | ||||
| void game_show_lives(GameData *game, bool show_lives); | ||||
| void game_show_level(GameData *game, bool show_level); | ||||
|  | ||||
| /* | ||||
|  * Sends a notification to the window associated with `game`. | ||||
|  * | ||||
|  * `message` - the notification message that will be displayed. | ||||
|  */ | ||||
| void game_window_notify(const GameData *game, const char *message); | ||||
|  | ||||
| /* | ||||
|  * Updates game score. | ||||
|  */ | ||||
| void game_update_score(GameData *game, long int points); | ||||
|  | ||||
| /* | ||||
|  * Sets game score to `val`. | ||||
|  */ | ||||
| void game_set_score(GameData *game, long int score); | ||||
|  | ||||
| /* | ||||
|  * Returns the game's current score. | ||||
|  */ | ||||
| @@ -377,7 +394,7 @@ void game_kill(ToxWindow *self); | ||||
|  * | ||||
|  * `packet_type` should be GP_Invite for an invite packet or GP_Data for all other game data. | ||||
|  */ | ||||
| int game_send_packet(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type); | ||||
| int game_packet_send(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type); | ||||
|  | ||||
| #endif // GAME_BASE | ||||
|  | ||||
|   | ||||
| @@ -33,7 +33,7 @@ | ||||
| #define CENT_MUSHROOMS_POP_CONSTANT 35000 | ||||
|  | ||||
| /* Max number of mushrooms */ | ||||
| #define CENT_MUSHROOMS_LENGTH (GAME_MAX_SQUARE_X * GAME_MAX_SQUARE_Y) | ||||
| #define CENT_MUSHROOMS_LENGTH (GAME_MAX_SQUARE_X_DEFAULT * GAME_MAX_SQUARE_X_DEFAULT) | ||||
|  | ||||
| /* Max number of individual centipedes at any given time */ | ||||
| #define CENT_MAX_NUM_HEADS    20 | ||||
| @@ -42,7 +42,7 @@ | ||||
| #define CENT_MAX_NUM_SEGMENTS 12 | ||||
|  | ||||
| /* Get a free life every time we get this many points. Needs to be > the most points we can get in a single shot. */ | ||||
| #define CENT_SCORE_ONE_UP 5000 | ||||
| #define CENT_SCORE_ONE_UP 7000 | ||||
|  | ||||
| /* Max number of lives we can have */ | ||||
| #define CENT_MAX_LIVES    6 | ||||
| @@ -53,6 +53,9 @@ | ||||
| /* Max speed of an enemy agent */ | ||||
| #define CENT_MAX_ENEMY_AGENT_SPEED 8 | ||||
|  | ||||
| /* Determines the overall speed of the game per game_set_update_interval() */ | ||||
| #define CENT_GAME_UPDATE_INTERVAL 14 | ||||
|  | ||||
| /* How often a head that reaches the bottom can repdoduce */ | ||||
| #define CENT_REPRODUCE_TIMEOUT 10 | ||||
|  | ||||
| @@ -64,7 +67,7 @@ | ||||
| #define CENT_BULLET_COLOUR    YELLOW | ||||
| #define CENT_BULLET_ATTR      A_BOLD | ||||
| #define CENT_BULLET_CHAR      '|' | ||||
| #define CENT_BULLET_SPEED     150 | ||||
| #define CENT_BULLET_SPEED     300 | ||||
|  | ||||
| #define CENT_BLASTER_ATTR     A_BOLD | ||||
| #define CENT_BLASTER_CHAR     'U' | ||||
| @@ -1593,6 +1596,7 @@ void cent_cb_kill(GameData *game, void *cb_data) | ||||
|     game_set_cb_update_state(game, NULL, NULL); | ||||
|     game_set_cb_render_window(game, NULL, NULL); | ||||
|     game_set_cb_kill(game, NULL, NULL); | ||||
|     game_set_cb_on_keypress(game, NULL, NULL); | ||||
|     game_set_cb_on_pause(game, NULL, NULL); | ||||
| } | ||||
|  | ||||
| @@ -1713,6 +1717,7 @@ static int cent_init_state(GameData *game, CentState *state) | ||||
|  | ||||
| int centipede_initialize(GameData *game) | ||||
| { | ||||
|     // note: If this changes we must update CENT_MUSHROOMS_LENGTH | ||||
|     if (game_set_window_shape(game, GW_ShapeSquare) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
| @@ -1728,7 +1733,7 @@ int centipede_initialize(GameData *game) | ||||
|     game_show_lives(game, true); | ||||
|     game_show_high_score(game, true); | ||||
|     game_increment_level(game); | ||||
|     game_set_update_interval(game, 10); | ||||
|     game_set_update_interval(game, CENT_GAME_UPDATE_INTERVAL); | ||||
|  | ||||
|     if (cent_init_state(game, state) == -1) { | ||||
|         free(state); | ||||
|   | ||||
| @@ -826,9 +826,7 @@ static bool chess_mock_move_valid(ChessState *state, const Player *player, Tile | ||||
|     chess_copy_piece(&to->piece, &from->piece); | ||||
|     from->piece.type = NoPiece; | ||||
|  | ||||
|     if (chess_player_in_check(state, player)) { | ||||
|         in_check = true;; | ||||
|     } | ||||
|     in_check = chess_player_in_check(state, player); | ||||
|  | ||||
|     from->piece.type = from_piece.type; | ||||
|     chess_copy_piece(&to->piece, &to_piece); | ||||
| @@ -1079,9 +1077,7 @@ static void chess_update_state(ChessState *state, Player *self, Player *other, c | ||||
|  | ||||
|     self->in_check = false; | ||||
|  | ||||
|     if (chess_player_in_check(state, other)) { | ||||
|         other->in_check = true; | ||||
|     } | ||||
|     other->in_check = chess_player_in_check(state, other); | ||||
|  | ||||
|     state->message_length = 0; | ||||
|     state->black_to_move ^= 1; | ||||
| @@ -1411,7 +1407,7 @@ static void chess_move_curs_left(ChessState *state) | ||||
| { | ||||
|     Board *board = &state->board; | ||||
|  | ||||
|     size_t new_x = state->curs_x - CHESS_TILE_SIZE_X; | ||||
|     int new_x = state->curs_x - CHESS_TILE_SIZE_X; | ||||
|  | ||||
|     if (new_x < board->x_left_bound) { | ||||
|         return; | ||||
| @@ -1424,7 +1420,7 @@ static void chess_move_curs_right(ChessState *state) | ||||
| { | ||||
|     Board *board = &state->board; | ||||
|  | ||||
|     size_t new_x = state->curs_x + CHESS_TILE_SIZE_X; | ||||
|     int new_x = state->curs_x + CHESS_TILE_SIZE_X; | ||||
|  | ||||
|     if (new_x > board->x_right_bound) { | ||||
|         return; | ||||
| @@ -1437,7 +1433,7 @@ static void chess_move_curs_up(ChessState *state) | ||||
| { | ||||
|     Board *board = &state->board; | ||||
|  | ||||
|     size_t new_y = state->curs_y - CHESS_TILE_SIZE_Y; | ||||
|     int new_y = state->curs_y - CHESS_TILE_SIZE_Y; | ||||
|  | ||||
|     if (new_y < board->y_top_bound) { | ||||
|         return; | ||||
| @@ -1450,7 +1446,7 @@ static void chess_move_curs_down(ChessState *state) | ||||
| { | ||||
|     Board *board = &state->board; | ||||
|  | ||||
|     size_t new_y = state->curs_y + CHESS_TILE_SIZE_Y; | ||||
|     int new_y = state->curs_y + CHESS_TILE_SIZE_Y; | ||||
|  | ||||
|     if (new_y >= board->y_bottom_bound) { | ||||
|         return; | ||||
| @@ -1716,7 +1712,6 @@ void chess_cb_render_window(GameData *game, WINDOW *win, void *cb_data) | ||||
|  | ||||
|     move(state->curs_y, state->curs_x); | ||||
|  | ||||
|  | ||||
|     curs_set(1); | ||||
|  | ||||
|     chess_draw_board(win, state); | ||||
| @@ -1773,6 +1768,7 @@ void chess_cb_on_keypress(GameData *game, int key, void *cb_data) | ||||
|         } | ||||
|  | ||||
|         case '\r': | ||||
|  | ||||
|         /* Intentional fallthrough */ | ||||
|         case ' ': { | ||||
|             chess_do_input(game, state); | ||||
| @@ -1813,7 +1809,7 @@ void chess_cb_kill(GameData *game, void *cb_data) | ||||
| static int chess_handle_opponent_move_packet(const GameData *game, ChessState *state, const uint8_t *data, | ||||
|         size_t length) | ||||
| { | ||||
|     if (length != CHESS_PACKET_MOVE_SIZE || data == NULL) { | ||||
|     if (length < CHESS_PACKET_MOVE_SIZE || data == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -1845,7 +1841,7 @@ static int chess_handle_opponent_move_packet(const GameData *game, ChessState *s | ||||
|     } | ||||
|  | ||||
|     if (chess_try_move_opponent(state, from_tile, to_tile) != 0) { | ||||
|         fprintf(stderr, "opponent tried to make an illegal move: %c%d-%c%d\n", from_l, from_n, to_l, to_n); | ||||
|         fprintf(stderr, "Chess opponent tried to make an illegal move: %c%d-%c%d\n", from_l, from_n, to_l, to_n); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -1853,13 +1849,41 @@ static int chess_handle_opponent_move_packet(const GameData *game, ChessState *s | ||||
|  | ||||
| } | ||||
|  | ||||
| static void chess_notify(const GameData *game, ChessPacketType type) | ||||
| { | ||||
|     const char *msg = NULL; | ||||
|  | ||||
|     switch (type) { | ||||
|         case CHESS_PACKET_INIT_ACCEPT_INVITE: { | ||||
|             msg = "Game on!"; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case CHESS_PACKET_RESIGN: { | ||||
|             msg = "Opponent has resigned"; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case CHESS_PACKET_MOVE_PIECE: { | ||||
|             msg = "Opponent has moved"; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         default: { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     game_window_notify(game, msg); | ||||
| } | ||||
|  | ||||
| static void chess_cb_on_packet(GameData *game, const uint8_t *data, size_t length, void *cb_data) | ||||
| { | ||||
|     if (length == 0 || data == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!cb_data) { | ||||
|     if (cb_data == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -1900,9 +1924,11 @@ static void chess_cb_on_packet(GameData *game, const uint8_t *data, size_t lengt | ||||
|  | ||||
|         default: { | ||||
|             fprintf(stderr, "Got unknown chess packet type: %d\n", type); | ||||
|             break; | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     chess_notify(game, type); | ||||
| } | ||||
|  | ||||
| static int chess_init_board(GameData *game, ChessState *state, bool self_is_white) | ||||
| @@ -2014,7 +2040,7 @@ static int chess_packet_send_resign(const GameData *game) | ||||
|     uint8_t data[1]; | ||||
|     data[0] = CHESS_PACKET_RESIGN; | ||||
|  | ||||
|     if (game_send_packet(game, data, 1, GP_Data) == -1) { | ||||
|     if (game_packet_send(game, data, 1, GP_Data) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -2030,7 +2056,7 @@ static int chess_packet_send_move(const GameData *game, const Tile *from, const | ||||
|     data[3] = to->chess_coords.L; | ||||
|     data[4] = to->chess_coords.N; | ||||
|  | ||||
|     if (game_send_packet(game, data, 5, GP_Data) == -1) { | ||||
|     if (game_packet_send(game, data, 5, GP_Data) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -2043,7 +2069,7 @@ static int chess_packet_send_invite(const GameData *game, bool self_is_white) | ||||
|     data[0] = CHESS_PACKET_INIT_SEND_INVITE; | ||||
|     data[1] = self_is_white ? Black : White; | ||||
|  | ||||
|     if (game_send_packet(game, data, 2, GP_Invite) == -1) { | ||||
|     if (game_packet_send(game, data, 2, GP_Invite) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -2055,7 +2081,7 @@ static int chess_packet_send_accept(const GameData *game) | ||||
|     uint8_t data[1]; | ||||
|     data[0] = CHESS_PACKET_INIT_ACCEPT_INVITE; | ||||
|  | ||||
|     if (game_send_packet(game, data, 1, GP_Data) == -1) { | ||||
|     if (game_packet_send(game, data, 1, GP_Data) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
| @@ -2130,4 +2156,3 @@ int chess_initialize(GameData *game, const uint8_t *init_data, size_t length) | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										676
									
								
								src/game_life.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										676
									
								
								src/game_life.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,676 @@ | ||||
| /*  game_life.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2021 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "game_life.h" | ||||
|  | ||||
| #define LIFE_DEFAULT_CELL_CHAR     'o' | ||||
| #define LIFE_CELL_DEFAULT_COLOUR    CYAN | ||||
| #define LIFE_DEFAULT_SPEED          25 | ||||
| #define LIFE_MAX_SPEED              40 | ||||
|  | ||||
| /* Determines the additional size of the grid beyond the visible boundaries. | ||||
|  * | ||||
|  * This buffer allows cells to continue growing off-screen giving the illusion of an | ||||
|  * infinite grid to a certain point. | ||||
|  */ | ||||
| #define LIFE_BOUNDARY_BUFFER        50 | ||||
|  | ||||
|  | ||||
| typedef struct Cell { | ||||
|     Coords     coords; | ||||
|     bool       alive; | ||||
|     bool       marked;  // true if cell should invert alive status at end of current cycle | ||||
|     int        display_char; | ||||
|     size_t     age; | ||||
| } Cell; | ||||
|  | ||||
| typedef struct LifeState { | ||||
|     TIME_MS    time_last_cycle; | ||||
|     size_t     speed; | ||||
|     size_t     generation; | ||||
|     bool       paused; | ||||
|  | ||||
|     Cell       **cells; | ||||
|     int        num_columns; | ||||
|     int        num_rows; | ||||
|  | ||||
|     int        curs_x; | ||||
|     int        curs_y; | ||||
|  | ||||
|     int        x_left_bound; | ||||
|     int        x_right_bound; | ||||
|     int        y_top_bound; | ||||
|     int        y_bottom_bound; | ||||
|  | ||||
|     short      display_candy; | ||||
|     int        colour; | ||||
| } LifeState; | ||||
|  | ||||
|  | ||||
| static void life_increase_speed(LifeState *state) | ||||
| { | ||||
|     if (state->speed < LIFE_MAX_SPEED) { | ||||
|         ++state->speed; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void life_decrease_speed(LifeState *state) | ||||
| { | ||||
|     if (state->speed > 1) { | ||||
|         --state->speed; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int life_get_display_char(const LifeState *state, const Cell *cell) | ||||
| { | ||||
|     if (state->display_candy == 1) { | ||||
|         if (cell->age == 1) { | ||||
|             return '.'; | ||||
|         } | ||||
|  | ||||
|         return '+'; | ||||
|     } | ||||
|  | ||||
|     if (state->display_candy == 2) { | ||||
|         if (cell->age == 1) { | ||||
|             return '.'; | ||||
|         } | ||||
|  | ||||
|         if (cell->age == 2) { | ||||
|             return '-'; | ||||
|         } | ||||
|  | ||||
|         if (cell->age == 3) { | ||||
|             return 'o'; | ||||
|         } | ||||
|  | ||||
|         return 'O'; | ||||
|     } | ||||
|  | ||||
|     return 'o'; | ||||
| } | ||||
|  | ||||
| static void life_toggle_display_candy(LifeState *state) | ||||
| { | ||||
|     state->display_candy = (state->display_candy + 1) % 3;  // magic number depends on life_get_display_char() | ||||
| } | ||||
|  | ||||
| static void life_cycle_colour(LifeState *state) | ||||
| { | ||||
|     switch (state->colour) { | ||||
|         case RED: { | ||||
|             state->colour = YELLOW; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case YELLOW: { | ||||
|             state->colour = GREEN; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case GREEN: { | ||||
|             state->colour = CYAN; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case CYAN: { | ||||
|             state->colour = BLUE; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case BLUE: { | ||||
|             state->colour = MAGENTA; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case MAGENTA: { | ||||
|             state->colour = RED; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         default: { | ||||
|             state->colour = RED; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static Cell *life_get_cell_at_coords(const LifeState *state, const int x, const int y) | ||||
| { | ||||
|     const int i = y - (state->y_top_bound - (LIFE_BOUNDARY_BUFFER / 2)); | ||||
|     const int j = x - (state->x_left_bound - (LIFE_BOUNDARY_BUFFER / 2)); | ||||
|  | ||||
|     if (i >= 0 && j >= 0) { | ||||
|         return &state->cells[i][j]; | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static void life_draw_cells(const GameData *game, WINDOW *win, LifeState *state) | ||||
| { | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(state->colour)); | ||||
|  | ||||
|     for (int i = LIFE_BOUNDARY_BUFFER / 2; i < state->num_rows - (LIFE_BOUNDARY_BUFFER / 2); ++i) { | ||||
|         for (int j = LIFE_BOUNDARY_BUFFER / 2; j < state->num_columns + 1 - (LIFE_BOUNDARY_BUFFER / 2); ++j) { | ||||
|             Cell *cell = &state->cells[i][j]; | ||||
|  | ||||
|             if (cell->alive) { | ||||
|                 Coords coords = cell->coords; | ||||
|                 mvwaddch(win, coords.y, coords.x, cell->display_char); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(state->colour)); | ||||
| } | ||||
|  | ||||
| static void life_toggle_cell(LifeState *state) | ||||
| { | ||||
|     Cell *cell = life_get_cell_at_coords(state, state->curs_x, state->curs_y); | ||||
|  | ||||
|     if (cell == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     cell->alive ^= 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Returns the number of live neighbours of cell at `i` `j` position. | ||||
|  * | ||||
|  * Returns NULL if cell is touching a border. | ||||
|  */ | ||||
| static int life_get_live_neighbours(const LifeState *state, const int i, const int j) | ||||
| { | ||||
|     Cell *n[8] = {0}; | ||||
|  | ||||
|     if (i > 0 && j > 0) { | ||||
|         n[0] = &state->cells[i - 1][j - 1]; | ||||
|     } | ||||
|  | ||||
|     if (i > 0) { | ||||
|         n[1] = &state->cells[i - 1][j]; | ||||
|     } | ||||
|  | ||||
|     if (i > 0 && j < state->num_columns - 1) { | ||||
|         n[2] = &state->cells[i - 1][j + 1]; | ||||
|     } | ||||
|  | ||||
|     if (j > 0) { | ||||
|         n[3] = &state->cells[i][j - 1]; | ||||
|     } | ||||
|  | ||||
|     if (j < state->num_columns - 1) { | ||||
|         n[4] = &state->cells[i][j + 1]; | ||||
|     } | ||||
|  | ||||
|     if (i < state->num_rows - 1 && j > 0) { | ||||
|         n[5] = &state->cells[i + 1][j - 1]; | ||||
|     } | ||||
|  | ||||
|     if (i < state->num_rows - 1) { | ||||
|         n[6] = &state->cells[i + 1][j]; | ||||
|     } | ||||
|  | ||||
|     if (i < state->num_rows - 1 && j < state->num_columns - 1) { | ||||
|         n[7] = &state->cells[i + 1][j + 1]; | ||||
|     } | ||||
|  | ||||
|     int count = 0; | ||||
|  | ||||
|     for (size_t i = 0; i < 8; ++i) { | ||||
|         if (n[i] == NULL) { | ||||
|             return 0; // If we're at a boundary kill cell | ||||
|         } | ||||
|  | ||||
|         if (n[i]->alive) { | ||||
|             ++count; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return count; | ||||
| } | ||||
|  | ||||
| static void life_restart(GameData *game, LifeState *state) | ||||
| { | ||||
|     for (int i = 0; i < state->num_rows; ++i) { | ||||
|         for (int j = 0; j < state->num_columns; ++j) { | ||||
|             Cell *cell = &state->cells[i][j]; | ||||
|             cell->alive = false; | ||||
|             cell->marked = false; | ||||
|             cell->display_char = LIFE_DEFAULT_CELL_CHAR; | ||||
|             cell->age = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     game_set_score(game, 0); | ||||
|  | ||||
|     state->generation = 0; | ||||
| } | ||||
|  | ||||
| static void life_do_cells(LifeState *state) | ||||
| { | ||||
|  | ||||
|     for (int i = 0; i < state->num_rows; ++i) { | ||||
|         for (int j = 0; j < state->num_columns; ++j) { | ||||
|             Cell *cell = &state->cells[i][j]; | ||||
|  | ||||
|             if (cell->marked) { | ||||
|                 cell->marked = false; | ||||
|                 cell->alive ^= 1; | ||||
|                 cell->age = cell->alive; | ||||
|                 cell->display_char = life_get_display_char(state, cell); | ||||
|             } else if (cell->alive) { | ||||
|                 ++cell->age; | ||||
|                 cell->display_char = life_get_display_char(state, cell); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void life_cycle(GameData *game, LifeState *state) | ||||
| { | ||||
|     if (state->generation == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     TIME_MS cur_time = get_time_millis(); | ||||
|  | ||||
|     if (!game_do_object_state_update(game, cur_time, state->time_last_cycle, state->speed)) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->time_last_cycle = get_time_millis(); | ||||
|  | ||||
|     ++state->generation; | ||||
|  | ||||
|     size_t live_cells = 0; | ||||
|  | ||||
|     for (int i = 0; i < state->num_rows; ++i) { | ||||
|         for (int j = 0; j < state->num_columns; ++j) { | ||||
|             Cell *cell = &state->cells[i][j]; | ||||
|  | ||||
|             int live_neighbours = life_get_live_neighbours(state, i, j); | ||||
|  | ||||
|             if (cell->alive) { | ||||
|                 if (!(live_neighbours == 2 || live_neighbours == 3)) { | ||||
|                     cell->marked = true; | ||||
|                 } else { | ||||
|                     ++live_cells; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (live_neighbours == 3) { | ||||
|                     cell->marked = true; | ||||
|                     ++live_cells; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (live_cells == 0) { | ||||
|         life_restart(game, state); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     life_do_cells(state); | ||||
|  | ||||
|     game_update_score(game, 1); | ||||
| } | ||||
|  | ||||
| static void life_start(GameData *game, LifeState *state) | ||||
| { | ||||
|     state->generation = 1; | ||||
| } | ||||
|  | ||||
| void life_cb_update_game_state(GameData *game, void *cb_data) | ||||
| { | ||||
|     if (!cb_data) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LifeState *state = (LifeState *)cb_data; | ||||
|  | ||||
|     life_cycle(game, state); | ||||
| } | ||||
|  | ||||
| void life_cb_render_window(GameData *game, WINDOW *win, void *cb_data) | ||||
| { | ||||
|     if (!cb_data) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LifeState *state = (LifeState *)cb_data; | ||||
|  | ||||
|     move(state->curs_y, state->curs_x); | ||||
|  | ||||
|     if (state->generation == 0 || state->paused) { | ||||
|         curs_set(1); | ||||
|     } | ||||
|  | ||||
|     life_draw_cells(game, win, state); | ||||
| } | ||||
|  | ||||
| static void life_move_curs_left(LifeState *state) | ||||
| { | ||||
|     int new_x = state->curs_x - 1; | ||||
|  | ||||
|     if (new_x < state->x_left_bound) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->curs_x = new_x; | ||||
| } | ||||
|  | ||||
| static void life_move_curs_right(LifeState *state) | ||||
| { | ||||
|     int new_x = state->curs_x + 1; | ||||
|  | ||||
|     if (new_x > state->x_right_bound) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->curs_x = new_x; | ||||
| } | ||||
|  | ||||
| static void life_move_curs_up(LifeState *state) | ||||
| { | ||||
|     int new_y = state->curs_y - 1; | ||||
|  | ||||
|     if (new_y < state->y_top_bound) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->curs_y = new_y; | ||||
| } | ||||
|  | ||||
| static void life_move_curs_down(LifeState *state) | ||||
| { | ||||
|     int new_y = state->curs_y + 1; | ||||
|  | ||||
|     if (new_y >= state->y_bottom_bound) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     state->curs_y = new_y; | ||||
| } | ||||
|  | ||||
| static void life_move_curs_up_left(LifeState *state) | ||||
| { | ||||
|     life_move_curs_up(state); | ||||
|     life_move_curs_left(state); | ||||
| } | ||||
|  | ||||
| static void life_move_curs_up_right(LifeState *state) | ||||
| { | ||||
|     life_move_curs_up(state); | ||||
|     life_move_curs_right(state); | ||||
| } | ||||
|  | ||||
| static void life_move_curs_down_right(LifeState *state) | ||||
| { | ||||
|     life_move_curs_down(state); | ||||
|     life_move_curs_right(state); | ||||
| } | ||||
|  | ||||
| static void life_move_curs_down_left(LifeState *state) | ||||
| { | ||||
|     life_move_curs_down(state); | ||||
|     life_move_curs_left(state); | ||||
| } | ||||
|  | ||||
| void life_cb_on_keypress(GameData *game, int key, void *cb_data) | ||||
| { | ||||
|     if (!cb_data) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LifeState *state = (LifeState *)cb_data; | ||||
|  | ||||
|     switch (key) { | ||||
|         case KEY_LEFT: { | ||||
|             life_move_curs_left(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_RIGHT: { | ||||
|             life_move_curs_right(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_DOWN: { | ||||
|             life_move_curs_down(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_UP: { | ||||
|             life_move_curs_up(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_HOME: { | ||||
|             life_move_curs_up_left(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_END: { | ||||
|             life_move_curs_down_left(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_PPAGE: { | ||||
|             life_move_curs_up_right(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case KEY_NPAGE: { | ||||
|             life_move_curs_down_right(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case '\r': { | ||||
|             if (state->generation > 0) { | ||||
|                 life_restart(game, state); | ||||
|             } else { | ||||
|                 life_start(game, state); | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case ' ': { | ||||
|             life_toggle_cell(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case '=': | ||||
|  | ||||
|         /* intentional fallthrough */ | ||||
|  | ||||
|         case '+': { | ||||
|             life_increase_speed(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case '-': | ||||
|  | ||||
|         /* intentional fallthrough */ | ||||
|  | ||||
|         case '_': { | ||||
|             life_decrease_speed(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case '\t': { | ||||
|             life_toggle_display_candy(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case '`': { | ||||
|             life_cycle_colour(state); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         default: { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void life_free_cells(LifeState *state) | ||||
| { | ||||
|     if (state->cells == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < state->num_rows; ++i) { | ||||
|         if (state->cells[i]) { | ||||
|             free(state->cells[i]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     free(state->cells); | ||||
| } | ||||
|  | ||||
| void life_cb_pause(GameData *game, bool is_paused, void *cb_data) | ||||
| { | ||||
|     if (!cb_data) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LifeState *state = (LifeState *)cb_data; | ||||
|  | ||||
|     state->paused = is_paused; | ||||
| } | ||||
|  | ||||
| void life_cb_kill(GameData *game, void *cb_data) | ||||
| { | ||||
|     if (!cb_data) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LifeState *state = (LifeState *)cb_data; | ||||
|  | ||||
|     life_free_cells(state); | ||||
|     free(state); | ||||
|  | ||||
|     game_set_cb_update_state(game, NULL, NULL); | ||||
|     game_set_cb_render_window(game, NULL, NULL); | ||||
|     game_set_cb_kill(game, NULL, NULL); | ||||
|     game_set_cb_on_keypress(game, NULL, NULL); | ||||
| } | ||||
|  | ||||
| static int life_init_state(GameData *game, LifeState *state) | ||||
| { | ||||
|     const int x_left = game_x_left_bound(game) ; | ||||
|     const int x_right = game_x_right_bound(game); | ||||
|     const int y_top = game_y_top_bound(game); | ||||
|     const int y_bottom = game_y_bottom_bound(game) + 1; | ||||
|  | ||||
|     state->x_left_bound = x_left; | ||||
|     state->x_right_bound = x_right; | ||||
|     state->y_top_bound = y_top; | ||||
|     state->y_bottom_bound = y_bottom; | ||||
|  | ||||
|     const int x_mid = x_left + ((x_right - x_left) / 2); | ||||
|     const int y_mid = y_top + ((y_bottom - y_top) / 2); | ||||
|  | ||||
|     state->curs_x = x_mid; | ||||
|     state->curs_y = y_mid; | ||||
|  | ||||
|     const int num_rows = (y_bottom - y_top) + LIFE_BOUNDARY_BUFFER; | ||||
|     const int num_columns = (x_right - x_left) + LIFE_BOUNDARY_BUFFER; | ||||
|  | ||||
|     if (num_rows <= 0 || num_columns <= 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     state->num_columns = num_columns; | ||||
|     state->num_rows = num_rows; | ||||
|  | ||||
|     state->cells = calloc(1, num_rows * sizeof(Cell *)); | ||||
|  | ||||
|     if (state->cells == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < num_rows; ++i) { | ||||
|         state->cells[i] = calloc(1, num_columns * sizeof(Cell)); | ||||
|  | ||||
|         if (state->cells[i] == NULL) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         for (int j = 0; j < num_columns; ++j) { | ||||
|             state->cells[i][j].coords.y = i + (state->y_top_bound - (LIFE_BOUNDARY_BUFFER / 2)); | ||||
|             state->cells[i][j].coords.x = j + (state->x_left_bound - (LIFE_BOUNDARY_BUFFER / 2)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     state->speed = LIFE_DEFAULT_SPEED; | ||||
|     state->colour = LIFE_CELL_DEFAULT_COLOUR; | ||||
|  | ||||
|     life_restart(game, state); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int life_initialize(GameData *game) | ||||
| { | ||||
|     // Try best fit from largest to smallest before giving up | ||||
|     if (game_set_window_shape(game, GW_ShapeRectangleLarge) == -1) { | ||||
|         if (game_set_window_shape(game, GW_ShapeSquareLarge) == -1) { | ||||
|             if (game_set_window_shape(game, GW_ShapeRectangle) == -1) { | ||||
|                 if (game_set_window_shape(game, GW_ShapeSquare) == -1) { | ||||
|                     return -1; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LifeState *state = calloc(1, sizeof(LifeState)); | ||||
|  | ||||
|     if (state == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (life_init_state(game, state) == -1) { | ||||
|         life_free_cells(state); | ||||
|         free(state); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     game_set_update_interval(game, 40); | ||||
|     game_show_score(game, true); | ||||
|  | ||||
|     game_set_cb_update_state(game, life_cb_update_game_state, state); | ||||
|     game_set_cb_render_window(game, life_cb_render_window, state); | ||||
|     game_set_cb_on_keypress(game, life_cb_on_keypress, state); | ||||
|     game_set_cb_on_pause(game, life_cb_pause, state); | ||||
|     game_set_cb_kill(game, life_cb_kill, state); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/game_life.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/game_life.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /*  game_life.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2021 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef GAME_LIFE | ||||
| #define GAME_LIFE | ||||
|  | ||||
| #include "game_base.h" | ||||
|  | ||||
| int life_initialize(GameData *game); | ||||
|  | ||||
| #endif // GAME_LIFE | ||||
|  | ||||
| @@ -29,8 +29,8 @@ | ||||
| #include "game_snake.h" | ||||
| #include "misc_tools.h" | ||||
|  | ||||
| #define SNAKE_MAX_SNAKE_LENGTH     (GAME_MAX_SQUARE_X * GAME_MAX_SQUARE_Y) | ||||
| #define SNAKE_AGENT_MAX_LIST_SIZE  (GAME_MAX_SQUARE_X * GAME_MAX_SQUARE_Y) | ||||
| #define SNAKE_MAX_SNAKE_LENGTH     (GAME_MAX_SQUARE_X_DEFAULT * GAME_MAX_SQUARE_Y_DEFAULT) | ||||
| #define SNAKE_AGENT_MAX_LIST_SIZE  (GAME_MAX_SQUARE_X_DEFAULT * GAME_MAX_SQUARE_Y_DEFAULT) | ||||
|  | ||||
| #define SNAKE_DEFAULT_SNAKE_SPEED 6 | ||||
| #define SNAKE_DEFAULT_AGENT_SPEED 1 | ||||
| @@ -801,6 +801,7 @@ void snake_cb_kill(GameData *game, void *cb_data) | ||||
|     game_set_cb_update_state(game, NULL, NULL); | ||||
|     game_set_cb_render_window(game, NULL, NULL); | ||||
|     game_set_cb_kill(game, NULL, NULL); | ||||
|     game_set_cb_on_keypress(game, NULL, NULL); | ||||
|     game_set_cb_on_pause(game, NULL, NULL); | ||||
| } | ||||
|  | ||||
| @@ -849,6 +850,7 @@ static void snake_initialize_snake_head(const GameData *game, Snake *snake) | ||||
|  | ||||
| int snake_initialize(GameData *game) | ||||
| { | ||||
|     // note: if this changes we must update SNAKE_MAX_SNAKE_LENGTH and SNAKE_AGENT_MAX_LIST_SIZE | ||||
|     if (game_set_window_shape(game, GW_ShapeSquare) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|   | ||||
| @@ -104,22 +104,28 @@ void game_util_move_coords(Direction direction, Coords *coords) | ||||
| { | ||||
|     switch (direction) { | ||||
|         case NORTH: { | ||||
|             --(coords->y); | ||||
|             if (coords->y > 0) { | ||||
|                 --(coords->y); | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case SOUTH: { | ||||
|             ++(coords->y); | ||||
|             ++(coords->y);  // Will rollover if you do something stupid | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case EAST: { | ||||
|             ++(coords->x); | ||||
|             ++(coords->x);  // Will rollover if you do something stupid | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         case WEST: { | ||||
|             --(coords->x); | ||||
|             if (coords->x > 0) { | ||||
|                 --(coords->x); | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #define GAME_UTIL | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <time.h> | ||||
|  | ||||
| typedef struct Coords { | ||||
|     int x; | ||||
| @@ -100,4 +101,3 @@ size_t game_util_pack_u32(uint8_t *bytes, uint32_t v); | ||||
| size_t game_util_unpack_u32(const uint8_t *bytes, uint32_t *v); | ||||
|  | ||||
| #endif  // GAME_UTIL | ||||
|  | ||||
|   | ||||
| @@ -367,19 +367,8 @@ void cmd_game(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bool force_small = false; | ||||
|  | ||||
|     if (argc >= 2) { | ||||
|         force_small = strcasecmp(argv[2], "small") == 0; | ||||
|  | ||||
|         if (!force_small) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown argument."); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     uint32_t id = rand(); | ||||
|     int ret = game_initialize(self, m, type, id, NULL, 0, force_small); | ||||
|     int ret = game_initialize(self, m, type, id, NULL, 0); | ||||
|  | ||||
|     switch (ret) { | ||||
|         case 0: { | ||||
| @@ -387,8 +376,7 @@ void cmd_game(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA | ||||
|         } | ||||
|  | ||||
|         case -1: { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                           "Window is too small. Try enlarging your window or re-running the command with the 'small' argument."); | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Window is too small."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -174,7 +174,7 @@ static void help_draw_global(ToxWindow *self) | ||||
|     wprintw(win, "  /decline <id>              : Decline friend request\n"); | ||||
|     wprintw(win, "  /requests                  : List pending friend requests\n"); | ||||
|     wprintw(win, "  /connect <ip> <port> <key> : Manually connect to a DHT node\n"); | ||||
|     wprintw(win, "  /status <type> <msg>       : Set status with optional note\n"); | ||||
|     wprintw(win, "  /status <type>             : Set status (Online, Busy, Away)\n"); | ||||
|     wprintw(win, "  /note <msg>                : Set a personal note\n"); | ||||
|     wprintw(win, "  /nick <nick>               : Set your nickname\n"); | ||||
|     wprintw(win, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||
|   | ||||
| @@ -60,7 +60,7 @@ static const char *glob_cmd_list[] = { | ||||
|     "/decline", | ||||
|     "/exit", | ||||
|     "/conference", | ||||
| #ifdef GAME | ||||
| #ifdef GAMES | ||||
|     "/game", | ||||
| #endif | ||||
|     "/help", | ||||
|   | ||||
| @@ -383,6 +383,7 @@ static void init_term(void) | ||||
|         init_pair(BLACK_BG, COLOR_BLACK, bar_bg_color); | ||||
|         init_pair(PURPLE_BG, COLOR_MAGENTA, bar_bg_color); | ||||
|         init_pair(BAR_TEXT, bar_fg_color, bar_bg_color); | ||||
|         init_pair(BAR_SOLID, bar_bg_color, bar_bg_color); | ||||
|         init_pair(BAR_ACCENT, bar_accent_color, bar_bg_color); | ||||
|         init_pair(BAR_NOTIFY, bar_notify_color, bar_bg_color); | ||||
|         init_pair(STATUS_ONLINE, COLOR_GREEN, bar_bg_color); | ||||
|   | ||||
| @@ -457,7 +457,12 @@ void del_window(ToxWindow *w) | ||||
|     refresh(); | ||||
|  | ||||
|     if (num_active_windows > 0) { | ||||
|         if (active_window_index == 2) {    // if closing current window would bring us to friend list | ||||
|             set_next_window(-1);           // skip back to the home window instead. FIXME: magic numbers | ||||
|         } | ||||
|  | ||||
|         set_next_window(-1); | ||||
|  | ||||
|         --num_active_windows; | ||||
|     } | ||||
| } | ||||
| @@ -519,6 +524,12 @@ void on_window_resize(void) | ||||
|  | ||||
|             getmaxyx(w->window, y2, x2); | ||||
|  | ||||
|             if (y2 <= 0 || x2 <= 0) { | ||||
|                 fprintf(stderr, "Failed to resize game window: max_x: %d, max_y: %d\n", x2, y2); | ||||
|                 delwin(w->window); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, COLS, LINES - 2, 0); | ||||
|             w->game->window = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); | ||||
|             continue; | ||||
| @@ -549,7 +560,7 @@ void on_window_resize(void) | ||||
|         if (y2 <= 0 || x2 <= 0) { | ||||
|             fprintf(stderr, "Failed to resize window: max_x: %d, max_y: %d\n", x2, y2); | ||||
|             delwin(w->window); | ||||
|             return; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (w->show_peerlist) { | ||||
| @@ -790,7 +801,6 @@ void draw_active_window(Tox *m) | ||||
|  | ||||
|         if (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab) { | ||||
|             set_next_window(ch); | ||||
|             ch = KEY_F(2); | ||||
|         } | ||||
|  | ||||
|         a->onKey(a, m, ch, false); | ||||
| @@ -892,18 +902,30 @@ void kill_all_windows(Tox *m) | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (w->type == WINDOW_TYPE_CHAT) { | ||||
|             kill_chat_window(w, m); | ||||
|         } else if (w->type == WINDOW_TYPE_CONFERENCE) { | ||||
|             free_conference(w, w->num); | ||||
|         } | ||||
|         switch (w->type) { | ||||
|             case WINDOW_TYPE_CHAT: { | ||||
|                 kill_chat_window(w, m); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case WINDOW_TYPE_CONFERENCE: { | ||||
|                 free_conference(w, w->num); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| #ifdef GAMES | ||||
|         else if (w->type == WINDOW_TYPE_GAME) { | ||||
|             game_kill(w); | ||||
|         } | ||||
|  | ||||
|             case WINDOW_TYPE_GAME: { | ||||
|                 game_kill(w); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| #endif // GAMES | ||||
|  | ||||
|             default: { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* TODO: use enum instead of magic indices */ | ||||
|   | ||||
| @@ -72,6 +72,7 @@ typedef enum { | ||||
|     STATUS_BUSY, | ||||
|     STATUS_AWAY, | ||||
|     BAR_NOTIFY, | ||||
|     BAR_SOLID, | ||||
| } C_COLOURS; | ||||
|  | ||||
| /* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */ | ||||
| @@ -310,5 +311,5 @@ void draw_window_bar(ToxWindow *self); | ||||
|    call at least once per second */ | ||||
| void refresh_inactive_windows(void); | ||||
|  | ||||
| #endif // WINWDOWS_H | ||||
| #endif // WINDOWS_H | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user