2020-12-11 07:46:01 +01:00
|
|
|
/* game_base.h
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2020 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_BASE
|
|
|
|
#define GAME_BASE
|
|
|
|
|
|
|
|
#include <ncurses.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <tox/tox.h>
|
|
|
|
|
|
|
|
#include "game_util.h"
|
|
|
|
#include "windows.h"
|
|
|
|
|
2021-07-27 20:15:36 +02:00
|
|
|
#define GAME_BORDER_COLOUR BAR_SOLID
|
2020-12-19 21:40:00 +01:00
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
|
2021-06-03 07:19:49 +02:00
|
|
|
/* 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)
|
2020-12-11 07:46:01 +01:00
|
|
|
|
|
|
|
/* Max size of a default size rectangle game window */
|
2021-06-03 07:19:49 +02:00
|
|
|
#define GAME_MAX_RECT_Y_DEFAULT 24
|
|
|
|
#define GAME_MAX_RECT_X_DEFAULT (GAME_MAX_RECT_Y_DEFAULT * 4)
|
2020-12-11 07:46:01 +01:00
|
|
|
|
2021-06-03 07:19:49 +02:00
|
|
|
/* 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)
|
2020-12-11 07:46:01 +01:00
|
|
|
|
|
|
|
/* Maximum length of a game message set with game_set_message() */
|
|
|
|
#define GAME_MAX_MESSAGE_SIZE 64
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
/* Default number of seconds a game message is displayed for */
|
2020-12-11 07:46:01 +01:00
|
|
|
#define GAME_MESSAGE_DEFAULT_TIMEOUT 3
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
|
2021-06-03 07:19:49 +02:00
|
|
|
/***** START NETWORKING CONSTANTS *****/
|
2021-01-17 19:29:13 +01:00
|
|
|
|
|
|
|
/* 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))
|
|
|
|
|
|
|
|
/* Max size of a game packet including the header */
|
|
|
|
#define GAME_MAX_PACKET_SIZE 1024
|
|
|
|
|
|
|
|
/* Max size of a game packet payload */
|
|
|
|
#define GAME_MAX_DATA_SIZE (GAME_MAX_PACKET_SIZE - GAME_PACKET_HEADER_SIZE - 1)
|
|
|
|
|
|
|
|
/* Current version of networking protocol */
|
|
|
|
#define GAME_NETWORKING_VERSION 0x01
|
|
|
|
|
2021-06-03 07:19:49 +02:00
|
|
|
/***** END NETWORKING CONSTANTS *****/
|
|
|
|
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
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);
|
|
|
|
typedef void cb_game_pause(GameData *game, bool is_paused, void *cb_data);
|
|
|
|
typedef void cb_game_key_press(GameData *game, int key, void *cb_data);
|
2021-01-17 19:29:13 +01:00
|
|
|
typedef void cb_game_on_packet(GameData *game, const uint8_t *data, size_t length, void *cb_data);
|
2020-12-11 07:46:01 +01:00
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
typedef enum GamePacketType {
|
|
|
|
GP_Invite = 0u,
|
|
|
|
GP_Data,
|
|
|
|
} GamePacketType;
|
2020-12-11 07:46:01 +01:00
|
|
|
|
|
|
|
typedef enum GameWindowShape {
|
2020-12-19 21:40:00 +01:00
|
|
|
GW_ShapeSquare = 0u,
|
2021-06-03 07:19:49 +02:00
|
|
|
GW_ShapeSquareLarge,
|
2020-12-11 07:46:01 +01:00
|
|
|
GW_ShapeRectangle,
|
2021-06-03 07:19:49 +02:00
|
|
|
GW_ShapeRectangleLarge,
|
2020-12-11 07:46:01 +01:00
|
|
|
GW_ShapeInvalid,
|
|
|
|
} GameWindowShape;
|
|
|
|
|
|
|
|
typedef enum GameStatus {
|
2020-12-19 21:40:00 +01:00
|
|
|
GS_None = 0u,
|
2020-12-11 07:46:01 +01:00
|
|
|
GS_Paused,
|
|
|
|
GS_Running,
|
|
|
|
GS_Finished,
|
|
|
|
GS_Invalid,
|
|
|
|
} GameStatus;
|
|
|
|
|
|
|
|
typedef enum GameType {
|
2020-12-19 21:40:00 +01:00
|
|
|
GT_Centipede = 0u,
|
|
|
|
GT_Chess,
|
2021-06-02 05:00:00 +02:00
|
|
|
GT_Life,
|
2020-12-19 21:40:00 +01:00
|
|
|
GT_Snake,
|
2020-12-11 07:46:01 +01:00
|
|
|
GT_Invalid,
|
|
|
|
} GameType;
|
|
|
|
|
|
|
|
typedef struct GameMessage {
|
|
|
|
char message[GAME_MAX_MESSAGE_SIZE + 1];
|
|
|
|
size_t length;
|
|
|
|
const Coords *coords; // pointer to coords so we can track movement
|
|
|
|
Coords original_coords; // static coords at time of being set
|
|
|
|
time_t timeout;
|
|
|
|
time_t set_time;
|
|
|
|
int attributes;
|
|
|
|
int colour;
|
|
|
|
Direction direction;
|
|
|
|
bool sticky;
|
|
|
|
bool priority;
|
|
|
|
} GameMessage;
|
|
|
|
|
|
|
|
struct GameData {
|
|
|
|
TIME_MS last_frame_time;
|
|
|
|
TIME_MS update_interval; // determines the refresh rate (lower means faster)
|
|
|
|
long int score;
|
|
|
|
size_t high_score;
|
|
|
|
int lives;
|
|
|
|
size_t level;
|
|
|
|
GameStatus status;
|
|
|
|
GameType type;
|
2021-01-17 19:29:13 +01:00
|
|
|
bool is_multiplayer;
|
2020-12-11 07:46:01 +01:00
|
|
|
|
2020-12-19 21:40:00 +01:00
|
|
|
bool show_lives;
|
|
|
|
bool show_score;
|
|
|
|
bool show_high_score;
|
|
|
|
bool show_level;
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
GameMessage *messages;
|
|
|
|
size_t messages_size;
|
|
|
|
|
|
|
|
int game_max_x; // max dimensions of game window
|
|
|
|
int game_max_y;
|
2021-01-17 19:29:13 +01:00
|
|
|
|
|
|
|
int parent_max_x; // max dimensions of parent window
|
|
|
|
int parent_max_y;
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
int window_id;
|
|
|
|
WINDOW *window;
|
2021-01-17 19:29:13 +01:00
|
|
|
|
|
|
|
Tox *tox; // must be locked with Winthread mutex
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
GameWindowShape window_shape;
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
uint32_t id; // indentifies multiplayer instance
|
|
|
|
uint32_t friend_number; // friendnumber associated with parent window
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
cb_game_update_state *cb_game_update_state;
|
|
|
|
void *cb_game_update_state_data;
|
|
|
|
|
|
|
|
cb_game_render_window *cb_game_render_window;
|
|
|
|
void *cb_game_render_window_data;
|
|
|
|
|
|
|
|
cb_game_kill *cb_game_kill;
|
|
|
|
void *cb_game_kill_data;
|
|
|
|
|
|
|
|
cb_game_pause *cb_game_pause;
|
|
|
|
void *cb_game_pause_data;
|
|
|
|
|
|
|
|
cb_game_key_press *cb_game_key_press;
|
|
|
|
void *cb_game_key_press_data;
|
2021-01-17 19:29:13 +01:00
|
|
|
|
|
|
|
cb_game_on_packet *cb_game_on_packet;
|
|
|
|
void *cb_game_on_packet_data;
|
2020-12-11 07:46:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the callback for game state updates.
|
|
|
|
*/
|
|
|
|
void game_set_cb_update_state(GameData *game, cb_game_update_state *func, void *cb_data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the callback for frame rendering.
|
|
|
|
*/
|
|
|
|
void game_set_cb_render_window(GameData *game, cb_game_render_window *func, void *cb_data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the callback for game termination.
|
|
|
|
*/
|
|
|
|
void game_set_cb_kill(GameData *game, cb_game_kill *func, void *cb_data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the callback for the game pause event.
|
|
|
|
*/
|
|
|
|
void game_set_cb_on_pause(GameData *game, cb_game_pause *func, void *cb_data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the callback for the key press event.
|
|
|
|
*/
|
|
|
|
void game_set_cb_on_keypress(GameData *game, cb_game_key_press *func, void *cb_data);
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
/*
|
|
|
|
* Sets the callback for the game packet event.
|
|
|
|
*/
|
|
|
|
void game_set_cb_on_packet(GameData *game, cb_game_on_packet *func, void *cb_data);
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
/*
|
|
|
|
* Initializes game instance.
|
|
|
|
*
|
2021-01-17 19:29:13 +01:00
|
|
|
* `type` must be a valid GameType.
|
|
|
|
*
|
|
|
|
* `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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2020-12-11 07:46:01 +01:00
|
|
|
* Return 0 on success.
|
|
|
|
* Return -1 if screen is too small.
|
2021-01-17 19:29:13 +01:00
|
|
|
* Return -2 on network related error.
|
|
|
|
* Return -3 if multiplayer game is being initialized outside of a contact's window.
|
|
|
|
* Return -4 on other failure.
|
2020-12-11 07:46:01 +01:00
|
|
|
*/
|
2021-01-17 19:29:13 +01:00
|
|
|
int game_initialize(const ToxWindow *self, Tox *m, GameType type, uint32_t id, const uint8_t *multiplayer_data,
|
2021-06-03 07:19:49 +02:00
|
|
|
size_t length);
|
2020-12-11 07:46:01 +01:00
|
|
|
|
|
|
|
/*
|
2021-06-03 07:19:49 +02:00
|
|
|
* Sets game window to `shape`.
|
2020-12-11 07:46:01 +01:00
|
|
|
*
|
2021-06-03 07:19:49 +02:00
|
|
|
* This must be called on game initialization.
|
2020-12-11 07:46:01 +01:00
|
|
|
*
|
|
|
|
* Return 0 on success.
|
|
|
|
* Return -1 if window is too small or shape is invalid.
|
|
|
|
* Return -2 if function is called while the game state is valid.
|
|
|
|
*/
|
|
|
|
int game_set_window_shape(GameData *game, GameWindowShape shape);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the GameType associated with `game_string`.
|
|
|
|
*/
|
|
|
|
GameType game_get_type(const char *game_string);
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
/*
|
|
|
|
* Returns the name represented as a string associated with `type`.
|
|
|
|
*/
|
|
|
|
const char *game_get_name_string(GameType type);
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
/*
|
|
|
|
* Prints all available games to window associated with `self`.
|
|
|
|
*/
|
|
|
|
void game_list_print(ToxWindow *self);
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
/*
|
|
|
|
* Return true if game `type` has a multiplayer mode.
|
|
|
|
*/
|
|
|
|
bool game_type_is_multiplayer(GameType type);
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
/*
|
|
|
|
* Returns true if coordinates designated by `x` and `y` are within the game window boundaries.
|
|
|
|
*/
|
|
|
|
bool game_coordinates_in_bounds(const GameData *game, int x, int y);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put random coordinates that fit within the game window in `coords`.
|
|
|
|
*/
|
|
|
|
void game_random_coords(const GameData *game, Coords *coords);
|
|
|
|
|
|
|
|
/*
|
2020-12-19 21:40:00 +01:00
|
|
|
* Gets the current max dimensions of the game window.
|
2020-12-11 07:46:01 +01:00
|
|
|
*/
|
|
|
|
void game_max_x_y(const GameData *game, int *x, int *y);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the respective coordinate boundary of the game window.
|
|
|
|
*/
|
|
|
|
int game_y_bottom_bound(const GameData *game);
|
|
|
|
int game_y_top_bound(const GameData *game);
|
|
|
|
int game_x_right_bound(const GameData *game);
|
|
|
|
int game_x_left_bound(const GameData *game);
|
|
|
|
|
2020-12-19 21:40:00 +01:00
|
|
|
/*
|
|
|
|
* Toggle whether the respective game info is shown around the game window.
|
|
|
|
*/
|
|
|
|
void game_show_score(GameData *game, bool show_score);
|
|
|
|
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);
|
|
|
|
|
2021-04-24 00:10:53 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
/*
|
|
|
|
* Updates game score.
|
|
|
|
*/
|
|
|
|
void game_update_score(GameData *game, long int points);
|
|
|
|
|
2021-06-02 05:00:00 +02:00
|
|
|
/*
|
|
|
|
* Sets game score to `val`.
|
|
|
|
*/
|
|
|
|
void game_set_score(GameData *game, long int score);
|
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
/*
|
|
|
|
* Returns the game's current score.
|
|
|
|
*/
|
|
|
|
long int game_get_score(const GameData *game);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Increments level.
|
|
|
|
*
|
|
|
|
* This function should be called on initialization if game wishes to display level.
|
|
|
|
*/
|
|
|
|
void game_increment_level(GameData *game);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Updates lives with `amount`.
|
|
|
|
*
|
|
|
|
* If lives becomes negative the lives counter will not be drawn.
|
|
|
|
*/
|
|
|
|
void game_update_lives(GameData *game, int amount);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the remaining number of lives for the game.
|
|
|
|
*/
|
|
|
|
int game_get_lives(const GameData *game);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the current level.
|
|
|
|
*/
|
|
|
|
size_t game_get_current_level(const GameData *game);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the game status to `status`.
|
|
|
|
*/
|
|
|
|
void game_set_status(GameData *game, GameStatus status);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the game base update interval.
|
|
|
|
*
|
|
|
|
* Lower values of `update_interval` make the game faster, where 1 is the fastest and 50 is slowest.
|
|
|
|
* If this function is never called the game chooses a reasonable default.
|
|
|
|
*/
|
|
|
|
void game_set_update_interval(GameData *game, TIME_MS update_interval);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creates a message `message` of size `length` to be displayed at `coords` for `timeout` seconds.
|
|
|
|
*
|
|
|
|
* Message must be no greater than GAME_MAX_MESSAGE_SIZE bytes in length.
|
|
|
|
*
|
|
|
|
* If `sticky` is true the message will follow coords if they move.
|
|
|
|
*
|
|
|
|
* If `dir` is a valid direction, the message will be positioned a few squares away from `coords`
|
|
|
|
* so as to not overlap with its associated object.
|
|
|
|
*
|
|
|
|
* If `timeout` is zero, the default timeout value will be used.
|
|
|
|
*
|
|
|
|
* If `priority` true, messages will be printed on top of game objects.
|
|
|
|
*
|
|
|
|
* Return 0 on success.
|
|
|
|
* Return -1 on failure.
|
|
|
|
*/
|
|
|
|
int game_set_message(GameData *game, const char *message, size_t length, Direction dir, int attributes, int colour,
|
|
|
|
time_t timeout, const Coords *coords, bool sticky, bool priority);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns true if game should update an object's state according to its last moved time and current speed.
|
|
|
|
*
|
|
|
|
* This is used to independently control the speed of various game objects.
|
|
|
|
*/
|
|
|
|
bool game_do_object_state_update(const GameData *game, TIME_MS current_time, TIME_MS last_moved_time, TIME_MS speed);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the current wall time in milliseconds.
|
|
|
|
*/
|
|
|
|
TIME_MS get_time_millis(void);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ends game associated with `self` and cleans up.
|
|
|
|
*/
|
|
|
|
void game_kill(ToxWindow *self);
|
|
|
|
|
2021-01-17 19:29:13 +01:00
|
|
|
/*
|
|
|
|
* Sends a packet containing payload `data` of size `length` to the friendnumber associated with the game's
|
|
|
|
* parent window.
|
|
|
|
*
|
|
|
|
* `length` must not exceed GAME_MAX_DATA_SIZE bytes.
|
|
|
|
*
|
|
|
|
* `packet_type` should be GP_Invite for an invite packet or GP_Data for all other game data.
|
|
|
|
*/
|
2021-06-28 19:54:25 +02:00
|
|
|
int game_packet_send(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type);
|
2021-01-17 19:29:13 +01:00
|
|
|
|
2020-12-11 07:46:01 +01:00
|
|
|
#endif // GAME_BASE
|
2021-01-17 19:29:13 +01:00
|
|
|
|