diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d60ff51 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "files.associations": { + "*.scss": "postcss", + "*.sass": "bat", + "*.dot": "dot", + "*.m": "c", + "atomic": "c", + "*.tcc": "c", + "compare": "c", + "concepts": "c", + "functional": "c", + "memory": "c", + "numeric": "c", + "ostream": "c", + "stdexcept": "c", + "streambuf": "c", + "system_error": "c", + "type_traits": "c", + "typeinfo": "c", + "utility": "c" + } +} diff --git a/src/CRougeLite.c b/src/CRougeLite.c index 3ab208e..85bf154 100644 --- a/src/CRougeLite.c +++ b/src/CRougeLite.c @@ -22,6 +22,7 @@ #include "CRougeLite.h" // NOTE: declare global extern vars +#include "game/player.h" #include "system/atlas.h" #include "system/draw.h" #include "system/input.h" @@ -40,7 +41,7 @@ GameState *gameState = NULL; //======================================================== // Local Functions Headers //======================================================== -static void loadResources(Settings *settings); +static void loadResources(); static void update(); static void clearResources(); @@ -49,10 +50,12 @@ static void clearResources(); //======================================================== int main(void) { gameState = initGameState(); + initSettings(); + initDictionary(); Settings *settings = &(gameState->settings); InitWindow(settings->screenWidth, settings->screenHeight, "C rougelite game"); - // printf("TEST\n"); + initAtlas(); loadResources(settings); @@ -73,27 +76,29 @@ int main(void) { return 0; } -static void loadResources(Settings *settings) { +static void loadResources() { // load global assets + Settings settings = gameState->settings; InitAudioDevice(); music = LoadMusicStream("./src/" "./resources/ambient.ogg"); // NOTE: All paths must start from the src dir - SetMusicVolume(music, settings->musicVolume / 100.0); + SetMusicVolume(music, settings.musicVolume / 100.0); PlayMusicStream(music); - Player *player = initPlayer( - "Marcus", CAT, P_GUN, - (Vector2){settings->screenWidth / 2.0, settings->screenHeight / 2.0}, 0); + setupPlayers(); initEnemy(E_CIVILIAN, E_SWORD, (Vector2){128, 128}); initEnemy(E_FARMER, E_SWORD, - (Vector2){settings->screenWidth - 128 - 64, 128}); + (Vector2){settings.screenWidth - 128 - 64, 128}); } -static void update() { UpdateMusicStream(music); } +static void update() { + updatePlayers(); + UpdateMusicStream(music); +} static void clearResources() { clearGameState(); diff --git a/src/CRougeLite.h b/src/CRougeLite.h index a8063ad..ea38a63 100644 --- a/src/CRougeLite.h +++ b/src/CRougeLite.h @@ -14,33 +14,27 @@ #ifndef CROUGELITE_H #define CROUGELITE_H -#include -#include "defs.h" -#include "structs.h" -#include -#include -#include -#include + +#include "common.h" //======================================================== // Global Shared Variables //======================================================== extern Music music; -extern GameState* gameState; +extern GameState *gameState; //======================================================== // Global Functions //======================================================== // Init Functions GameState *initGameState(); +void initDictionary(); void initSettings(); -Player *initPlayer(const char *name, P_TYPE type, P_WEAPON weapon,Vector2 position, int ID); Enemy *initEnemy(E_TYPE type, E_WEAPON weapon, Vector2 position); -Bullet *initBullet(int ID, BulletInfo* bulletInfo, Vector2 src, Vector2 dest); +Bullet *initBullet(int ID, BulletInfo *bulletInfo, Vector2 src, Vector2 dest); // Clear Resources Functions void clearGameState(); -void clearPlayer(Player **player); void clearEnemy(Enemy **enemy); #endif // CROUGELITE_H diff --git a/src/common.h b/src/common.h index 8335585..c74dd9c 100644 --- a/src/common.h +++ b/src/common.h @@ -13,4 +13,19 @@ * * **************************************************************/ +#ifndef COMMON_H +#define COMMON_H +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "structs.h" + + + +#endif // COMMON_H diff --git a/src/defs.h b/src/defs.h index baf980a..eb1858d 100644 --- a/src/defs.h +++ b/src/defs.h @@ -25,7 +25,7 @@ #define DEFAULT_MAX_ENEMIES 100 #define DEFAULT_MAX_COMBAT_ACTIONS 100 -#define PLAYER_SPEED 5 +#define PLAYER_SPEED 10 #define MAX_FILENAME_LENGTH 256 diff --git a/src/game/player.c b/src/game/player.c new file mode 100644 index 0000000..84b124c --- /dev/null +++ b/src/game/player.c @@ -0,0 +1,218 @@ +/*************************************************************** + * + * + * ██████╗ ██╗ █████╗ ██╗ ██╗███████╗██████╗ + * ██╔══██╗██║ ██╔══██╗╚██╗ ██╔╝██╔════╝██╔══██╗ + * ██████╔╝██║ ███████║ ╚████╔╝ █████╗ ██████╔╝ + * ██╔═══╝ ██║ ██╔══██║ ╚██╔╝ ██╔══╝ ██╔══██╗ + * ██║ ███████╗██║ ██║ ██║ ███████╗██║ ██║ + * ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ + * + * Player Module Unit. (Game Object) + * Exposes the logic for the player object. + * + * - Setup Player + * - Update Player + * - Draw Player + * - Clear Player + * + **************************************************************/ + +#include "player.h" + +#include "../system/anime.h" +// FIXME: delete me later +#include "../system/init.h" +#include + +// *************************** +// Private Function Prototypes +// *************************** +static Player *initPlayer(const char *name, P_TYPE type, P_WEAPON weapon, + Vector2 position, int ID); +static void clearPlayer(Player **player); + +/* setupPlayers + * + * Setup the players in the game. Create a new player and adds it to + * the state of the game to draw and update. + * + */ +void setupPlayers() { + Player *player = initPlayer("Marcus", CAT, P_GUN, + (Vector2){gameState->settings.screenWidth / 2.0, + gameState->settings.screenHeight / 2.0}, + 0); +} + +/* drawPlayers + * + * Draw the players in the game. + * + */ +void drawPlayers() { + Player *players = gameState->players; + int player_num = gameState->numOfPlayers; + + // FIXME: This is a temporary solution + // Move this animation into the animator object + // And setup them up at the setup of the player. + SpriteAnimation idle = createSpriteAnimation(6, + (char *[]){ + "Meow-Knight_Idle_0_0", + "Meow-Knight_Idle_1_0", + "Meow-Knight_Idle_2_0", + "Meow-Knight_Idle_3_0", + "Meow-Knight_Idle_4_0", + "Meow-Knight_Idle_5_0", + }, + 6, true); + + SpriteAnimation walk = createSpriteAnimation(8, + (char *[]){ + "Meow-Knight_Run_0_0", + "Meow-Knight_Run_1_0", + "Meow-Knight_Run_2_0", + "Meow-Knight_Run_3_0", + "Meow-Knight_Run_4_0", + "Meow-Knight_Run_5_0", + "Meow-Knight_Run_6_0", + "Meow-Knight_Run_7_0", + }, + 12, true); + + + while (player_num--) { + Vector2 pos = players->object.transform.position; + bool flip = (players->drawDirection == -1) ? true : false; + if (players->isMoving) { + drawSpriteAnimationPro(&walk, (Rectangle){pos.x, pos.y, 64, 64}, + (Vector2){0, 0}, 0, WHITE, flip); + } else { + drawSpriteAnimationPro(&idle, (Rectangle){pos.x, pos.y, 64, 64}, + (Vector2){0, 0}, 0, WHITE, flip); + } + + players++; + } + + disposeSpriteAnimation(&idle); + disposeSpriteAnimation(&walk); +} + +/* updatePlayers + * + * Update the players in the game. + * it handles it's input and state too. + * + */ +void updatePlayers() { + Player *player = gameState->players; + Input input = player->input; + double speed = player->stats.speed; + + Vector2 direction = {0, 0}; + if (IsKeyDown(input.up)) + direction.y -= 1; + if (IsKeyDown(input.down)) + direction.y += 1; + if (IsKeyDown(input.left)) + direction.x -= 1; + if (IsKeyDown(input.right)) + direction.x += 1; + + Vector2 velocity = + Vector2Scale(Vector2Normalize(direction), speed); + + Vector2 position = Vector2Add( + player->object.transform.position, velocity); + + if (Vector2Length(velocity) > 0) { + player->isMoving = true; + } else { + player->isMoving = false; + } + + if (velocity.x < 0) { + player->drawDirection = -1; + } else { + player->drawDirection = 1; + } + + // NOTE: this makes the player unable to go out of frame + player->object.rigidBody.velocity = velocity; + player->object.transform.position = + Vector2Clamp(position, (Vector2){0, 0}, + (Vector2){gameState->settings.screenWidth - 64, + gameState->settings.screenHeight - 64}); + // FIXME: replace with sprite size +} + +void clearPlayers() { + int player_num = gameState->numOfPlayers; + Player *players = gameState->players; + + while (player_num--) { + printf("Deleting Player: %s\n", players->name); + clearPlayer(&players); + players++; + } +} + +// ***************** +// PRIVATE FUNCTIONS +// ***************** + +static void addPlayer(Player *player) { + Player *players = gameState->players; + players[gameState->numOfPlayers++] = *player; +} + +static Player *initPlayer(const char *name, P_TYPE type, P_WEAPON weapon, + Vector2 position, int ID) { + Settings settings = gameState->settings; + Dictionary *dict = gameState->characterDictionary; + Player *player = &(gameState->players[gameState->numOfPlayers++]); + int l = 0, r = NUM_OF_E_TYPE - 1; + + while (l <= r) { + int mid = l + (r - l) / 2; + int cmp = dict[mid].opcode - type; + if (!cmp) { + *player = dict[mid].entry.player; + break; + } + if (cmp < 0) + l = mid + 1; + else + r = mid - 1; + } + printf("Adding Player #%d\n", ID); + player->name = strdup(name); + player->ID = ID; + player->type = type; + Weapon selectedWeapon = initWeapon(weapon, true); + player->inventory = initInventory(); + player->inventory.weapons[player->inventory.currentNumOfWeapons++] = selectedWeapon; + player->object.transform.position = position; + player->score = 0; + player->drawDirection = 1; + player->direction = RIGHT; + player->fire = 0; + player->ID = ID; + player->experience = (Experience){.xp = 0, .level = 0}; + // TODO: Make dictionary for infos related to each type of character. + // Input + player->input = (Input){.up = KEY_W, .down = KEY_S, .left = KEY_A, .right = KEY_D, .action = KEY_E }; + + return player; +} + +static void clearPlayer(Player **player) { + if (player == NULL || *player == NULL) + return; + + free((*player)->name); + free(*player); + *player = NULL; +} diff --git a/src/game/player.h b/src/game/player.h new file mode 100644 index 0000000..f964a9b --- /dev/null +++ b/src/game/player.h @@ -0,0 +1,32 @@ +/*************************************************************** + * + * + * ██████╗ ██╗ █████╗ ██╗ ██╗███████╗██████╗ + * ██╔══██╗██║ ██╔══██╗╚██╗ ██╔╝██╔════╝██╔══██╗ + * ██████╔╝██║ ███████║ ╚████╔╝ █████╗ ██████╔╝ + * ██╔═══╝ ██║ ██╔══██║ ╚██╔╝ ██╔══╝ ██╔══██╗ + * ██║ ███████╗██║ ██║ ██║ ███████╗██║ ██║ + * ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ + * + * Player Module Header. (Game Object) + * Exposes the logic for the player object. + * + * - Setup Player + * - Update Player + * - Draw Player + * - Clear Player + * + **************************************************************/ + +#ifndef PLAYER_H +#define PLAYER_H + +#include "../CRougeLite.h" +#include "weapon.h" + +void setupPlayers(); +void drawPlayers(); +void updatePlayers(); +void clearPlayers(); + +#endif // PLAYER_H diff --git a/src/game/weapon.c b/src/game/weapon.c new file mode 100644 index 0000000..d07327a --- /dev/null +++ b/src/game/weapon.c @@ -0,0 +1,31 @@ +#include "weapon.h" + +Weapon initWeapon(int opcode, bool isPlayer) { + Dictionary *dict = (isPlayer ? gameState->playerWeaponDictionary + : gameState->enemyWeaponDictionary); + int l = 0, r = (isPlayer ? NUM_OF_P_WEAPON : NUM_OF_E_WEAPON) - 1; + + while (l <= r) { + int mid = l + (r - l) / 2; + int cmp = dict[mid].opcode - opcode; + if (!cmp) { + return dict[mid].entry.weapon; + } + if (cmp < 0) + l = mid + 1; + else + r = mid - 1; + } + + fprintf(stderr, "Error: Player Weapon Dictionary Failed"); + exit(EXIT_FAILURE); +} + +Inventory initInventory() { + Inventory inventory; + inventory.MAX_NUM_OF_WEAPONS = 2; + inventory.weapons = malloc(sizeof(Weapon)*inventory.MAX_NUM_OF_WEAPONS); + inventory.currentNumOfWeapons = 0; + inventory.currentWeapon = 0; + return inventory; +} \ No newline at end of file diff --git a/src/game/weapon.h b/src/game/weapon.h new file mode 100644 index 0000000..d6eaf68 --- /dev/null +++ b/src/game/weapon.h @@ -0,0 +1,12 @@ +#ifndef WEAPON_H +#define WEAPON_H + +#include "../CRougeLite.h" // NOTE: declare global extern vars + +Weapon initWeapon(int opcode, bool isPlayer); +Inventory initInventory(); +void useRangedWeapon(); +void useMeleeWeapon(); +void drawWeapon(); + +#endif // INPUT_H diff --git a/src/structs.h b/src/structs.h index ba99471..4d2337c 100644 --- a/src/structs.h +++ b/src/structs.h @@ -20,16 +20,112 @@ #include "defs.h" #include +/*============================================================================ + * ENUMS + * + * Put here all sort of types you want make sure that anychange here + * will be reflected in the whole code. (e.g. Switch cases, etc.) + *===========================================================================*/ + + // TODO: Make enum for all stats related to the specified types // instead of no encapsulation. -typedef struct CTransform { +// FIXME: this probably should be refactored and removed. +typedef enum +{ + W = 1, + A = 2, + S = 5, + D = 9, +} KEYS; + +typedef enum +{ + ACTION_NONE, + ACTION_BULLET, + ACTION_SLASH +} CombatActionType; + +typedef enum +{ + RANGED_WEAPON, + MELEE_WEAPON, + NUM_OF_WEAPON_TYPES +} WeaponType; + +typedef enum +{ + E_SWORD, + NUM_OF_E_WEAPON +} E_WEAPON; + +typedef enum +{ + CAT, + WEREWOLF, + PYROMANIAC, + KNIGHT, + NUM_OF_P_TYPE +} P_TYPE; + +typedef enum +{ + P_GUN, + P_LONG_SWORD, + NUM_OF_P_WEAPON +} P_WEAPON; + +typedef enum +{ + UP, + DOWN, + LEFT, + RIGHT, +} DIRECTIONS; + +typedef enum +{ + E_CIVILIAN, + E_FARMER, + E_KNIGHT, + NUM_OF_E_TYPE +} E_TYPE; + +typedef enum +{ + PATROL, + IDLE, + CHASE, + ATTACK, + FLEE +} State; + + +/*============================================================================ + * STRUCTS + * + * Here are all the structs (Think of them as object data that you use) + * Change here are expensive as these Almost types and used or all over + * the place so make sure every thing is OK after changes. + * + *===========================================================================*/ + + +// ********************** +// GENERAL ENGINE STRUCTS +// ********************** + +typedef struct CTransform +{ Vector2 position; float rotation; Vector2 scale; } CTransform; -typedef struct { +typedef struct +{ + Vector2 position; Vector2 velocity; Vector2 acceleration; float drag; @@ -38,19 +134,37 @@ typedef struct { // certain ways. } RigidBody2D; -typedef struct { +typedef struct +{ Vector2 offset; float width; float height; } Collider2D; -typedef struct { + +typedef struct AtlasImage +{ + char *filename; + Rectangle source; + Vector2 origin; + struct AtlasImage *next; +} AtlasImage; + +// FIXME: Rework SpriteRenderer or remove it if not used. +// Atlas Image could do the same work if it has the dest rect. +typedef struct +{ Texture2D texture; int width; int height; } SpriteRenderer; -typedef struct { + +// FIXME: Rework SpriteAnimation and animator +// Animator should handle the state and what animation to show. +// Animation is the animation itself. +typedef struct +{ char **frameNames; // Idk what is the type of the animation sprites. int currentFrame; int numOfFrames; @@ -61,45 +175,78 @@ typedef struct { bool finished; // NOTE: still not used } Animator; -typedef struct { +typedef struct SpriteAnimation +{ + int numOfFrames; + char **frameNames; + int currentFrame; // NOTE: still not used + int framesPerSecond; + bool loop; // NOTE: still not used + bool finished; // NOTE: still not used +} SpriteAnimation; + + +typedef struct +{ + int up; + int down; + int left; + int right; + int shoot; + int action; +} Input; + +typedef struct +{ + CTransform transform; + RigidBody2D rigidBody; + Collider2D collider; + SpriteRenderer spriteRenderer; + Animator animator; +} GameObject; + + +// ********************* +// GAME SPECIFIC STRUCTS +// ********************* + +typedef struct +{ int currentHealth; int maxHealth; } Health; // IDK if those affect the other structs or not (like leveling up) -typedef struct { +typedef struct +{ float power; float speed; float cooldown; } Attack; -typedef struct { +typedef struct +{ int value; int nearHitValue; // Blocked on the last second. // TODO: Add defense for different type of attacks? } Defense; -typedef struct { +typedef struct +{ int xp; int level; } Experience; -typedef struct { +typedef struct +{ Health health; Attack attack; Defense defense; + double speed; } Stats; -typedef struct { - int up; - int down; - int left; - int right; - int shoot; - int action; -} Input; - -typedef struct { +typedef struct +{ float bulletSpeed; float bulletDamage; float bulletRange; @@ -109,14 +256,16 @@ typedef struct { Collider2D collider; } BulletInfo; -typedef struct { +typedef struct +{ int playerID; BulletInfo bulletInfo; Vector2 startPosition; // To know if the bullet exceeded the range. CTransform transform; } Bullet; -typedef struct { +typedef struct +{ float slashRange; float slashDamage; bool isActive; @@ -124,76 +273,72 @@ typedef struct { Collider2D collider; } SlashInfo; -typedef struct { +typedef struct +{ int playerID; SlashInfo slashInfo; CTransform transform; } Slash; -typedef union { +typedef union +{ Bullet bullet; Slash slash; } CombatActionUnion; -typedef enum { ACTION_NONE, ACTION_BULLET, ACTION_SLASH } CombatActionType; - -typedef struct { +typedef struct +{ float angle; CombatActionUnion action; CombatActionType type; } CombatAction; -typedef struct { +typedef struct +{ int damage; float cooldown; float lastUseTime; SpriteRenderer weaponSprite; } WeaponStats; -typedef struct { +typedef struct +{ WeaponStats stats; BulletInfo bulletInfo; int maxAmmo; int ammo; } RangedWeapon; -typedef struct { +typedef struct +{ WeaponStats stats; SlashInfo slashInfo; } MeleeWeapon; -typedef enum { RANGED_WEAPON, MELEE_WEAPON, NUM_OF_WEAPON_TYPES } WeaponType; - -typedef union { +typedef union +{ RangedWeapon ranged; MeleeWeapon melee; } WeaponUnion; -typedef struct { +typedef struct +{ const char *name; WeaponType type; WeaponUnion weapon; } Weapon; -typedef struct { +typedef struct +{ int MAX_NUM_OF_WEAPONS; int currentNumOfWeapons; + int currentWeapon; Weapon *weapons; } Inventory; -typedef struct { - Transform transform; - RigidBody2D rigidBody; - Collider2D collider; - SpriteRenderer spriteRenderer; - Animator animator; - Stats stats; - Weapon weapon; -} GameObject; - -typedef enum { PATROL, IDLE, CHASE, ATTACK, FLEE } State; -typedef struct { +typedef struct +{ Vector2 patrolStart; Vector2 patrolEnd; float detectionRange; @@ -205,24 +350,20 @@ typedef struct { State state; } EnemyAI; -typedef struct { + +typedef struct +{ char *name; GameObject object; + Stats stats; + Weapon weapon; + E_TYPE type; EnemyAI ai; + int drawDirection; // 1 for right, -1 for left } Enemy; -typedef enum { CAT, WEREWOLF, PYROMANIAC, KNIGHT, NUM_OF_P_TYPE } P_TYPE; - -typedef enum { P_GUN, P_LONG_SWORD, NUM_OF_P_WEAPON } P_WEAPON; - -typedef enum { - UP, - DOWN, - LEFT, - RIGHT, -} DIRECTIONS; - -typedef struct { +typedef struct +{ // Player Info char *name; int ID; @@ -231,6 +372,8 @@ typedef struct { P_TYPE type; // Player Stats + Stats stats; + Inventory inventory; GameObject object; Input input; Experience experience; @@ -241,11 +384,12 @@ typedef struct { DIRECTIONS direction; // to get info on the direction the player is facing. } Player; -typedef enum { E_CIVILIAN, E_FARMER, E_KNIGHT, NUM_OF_E_TYPE } E_TYPE; - -typedef enum { E_SWORD, NUM_OF_E_WEAPON } E_WEAPON; +// ****************** +// GAME STATE STRUCTS +// ****************** -typedef struct { +typedef struct +{ int screenWidth; int screenHeight; bool fullscreen; @@ -255,25 +399,22 @@ typedef struct { bool sfx_on; } Settings; -typedef struct AtlasImage { - char *filename; - Rectangle source; - Vector2 origin; - struct AtlasImage *next; -} AtlasImage; - -typedef union { +typedef union +{ Weapon weapon; Enemy enemy; - GameObject character; + Player player; } DictionaryEntry; -typedef struct { +typedef struct +{ int opcode; DictionaryEntry entry; } Dictionary; -typedef struct { + +typedef struct +{ int numOfPlayers; Player *players; @@ -286,8 +427,8 @@ typedef struct { int level; bool isGameOver; bool isFinished; - Texture2D atlasTexture; - AtlasImage *atlasImages; + Texture2D atlasTexture; // The image atals containaing all the sprites and animations + AtlasImage *atlasImages; // Linked List of individual sprites and animations data Dictionary *playerWeaponDictionary; Dictionary *enemyWeaponDictionary; diff --git a/src/system/atlas.c b/src/system/atlas.c index 49828fa..eb9f92a 100644 --- a/src/system/atlas.c +++ b/src/system/atlas.c @@ -19,17 +19,15 @@ #include void initAtlas() { - GameState *game = getGameSystemInstance(); - game->atlasTexture = LoadTexture("./src/resources/gfx/atlas.png"); + gameState->atlasTexture = LoadTexture("./src/resources/gfx/atlas.png"); loadAtlasData(); } void loadAtlasData() { - GameState *game = getGameSystemInstance(); char *atlasData = LoadFileText("./src/resources/gfx/atlas.rtpa"); char *line = strtok(atlasData, "\n"); - AtlasImage *images = game->atlasImages; + AtlasImage *images = gameState->atlasImages; while (line != NULL) { if (line[0] == '#' || line[0] == 'a') { @@ -51,7 +49,7 @@ void loadAtlasData() { sprite->source = (Rectangle){width, height, x, y}; sprite->next = NULL; if (images == NULL) { - game->atlasImages = sprite; + gameState->atlasImages = sprite; images = sprite; } else { images->next = sprite; @@ -66,8 +64,7 @@ void loadAtlasData() { } AtlasImage getAtlasImage(char *filename) { - GameState *game = getGameSystemInstance(); - AtlasImage *images = game->atlasImages; + AtlasImage *images = gameState->atlasImages; while (images != NULL) { if (strcmp(images->filename, filename) == 0) { return *images; diff --git a/src/system/clearResources.c b/src/system/clearResources.c index 8d86981..e52f3d7 100644 --- a/src/system/clearResources.c +++ b/src/system/clearResources.c @@ -14,6 +14,7 @@ *********************************************************/ #include "../CRougeLite.h" // NOTE: declare global extern vars +#include "../game/player.h" //======================================================== // LOCAL VARIABLE DIFINATIONS (local to this file) @@ -24,31 +25,22 @@ //======================================================== void clearGameState() { - GameState *game = getGameSystemInstance(); - int player_num = game->numOfPlayers; - int enemy_num = game->numOfEnemies; - Player *players = game->players; - Enemy *enemies = game->enemies; - while (player_num--) { - printf("Deleting Player: %s\n", players->name); - clearPlayer(&players); - players++; - } + int enemy_num = gameState->numOfEnemies; + Enemy *enemies = gameState->enemies; + + clearPlayers(); while (enemy_num--) { - printf("Deleting Enemy of Type: %d\n", enemies->type); + // printf("Deleting Enemy of Type: %d\n", enemies->name); clearEnemy(&enemies); enemies++; } -} - -void clearPlayer(Player **player) { - if (player == NULL || *player == NULL) - return; - free((*player)->name); - free(*player); - *player = NULL; + free(gameState->characterDictionary); + free(gameState->enemyDictionary); + free(gameState->playerWeaponDictionary); + free(gameState->enemyWeaponDictionary); + free(gameState); } void clearEnemy(Enemy **enemy) { diff --git a/src/system/draw.c b/src/system/draw.c index 5e573d3..d683a86 100644 --- a/src/system/draw.c +++ b/src/system/draw.c @@ -1,99 +1,40 @@ #include "draw.h" +#include "../game/player.h" #include "anime.h" #include "atlas.h" #include void DrawAtlasSpritePro(char *filename, Rectangle dest, Vector2 origin, - float rotation, Color tint, bool flipX) -{ - Game_System *game = getGameSystemInstance(); + float rotation, Color tint, bool flipX) { AtlasImage image = getAtlasImage(filename); - if (image.filename != NULL) - { - if (flipX) - { + if (image.filename != NULL) { + if (flipX) { image.source.width *= -1; } - DrawTexturePro(game->atlasTexture, image.source, dest, origin, rotation, - tint); + DrawTexturePro(gameState->atlasTexture, image.source, dest, origin, + rotation, tint); DrawRectangleLines(dest.x, dest.y, dest.width, dest.height, RED); } } -static void drawPlayers(Game_System *game) -{ - Player *players = game->players; - int player_num = game->numOfPlayers; - - SpriteAnimation idle = createSpriteAnimation(6, - (char *[]){ - "Meow-Knight_Idle_0_0", - "Meow-Knight_Idle_1_0", - "Meow-Knight_Idle_2_0", - "Meow-Knight_Idle_3_0", - "Meow-Knight_Idle_4_0", - "Meow-Knight_Idle_5_0", - }, - 6, true); - - SpriteAnimation walk = createSpriteAnimation(8, - (char *[]){ - "Meow-Knight_Run_0_0", - "Meow-Knight_Run_1_0", - "Meow-Knight_Run_2_0", - "Meow-Knight_Run_3_0", - "Meow-Knight_Run_4_0", - "Meow-Knight_Run_5_0", - "Meow-Knight_Run_6_0", - "Meow-Knight_Run_7_0", - }, - 8, true); - - while (player_num--) - { - Vector2 pos = players->position; - bool flip = (players->drawDirection == -1) ? true : false; - if (players->isMoving) - { - drawSpriteAnimationPro(&walk, (Rectangle){pos.x, pos.y, 64, 64}, - (Vector2){0, 0}, 0, WHITE, flip); - } - else - { - drawSpriteAnimationPro(&idle, (Rectangle){pos.x, pos.y, 64, 64}, - (Vector2){0, 0}, 0, WHITE, flip); - } - - players++; - } - - disposeSpriteAnimation(&idle); - disposeSpriteAnimation(&walk); -} - -static void drawEnemies(Game_System *game) -{ - Enemy *enemies = game->enemies; - int enemy_num = game->num_of_enemies; +static void drawEnemies() { + Enemy *enemies = gameState->enemies; + int enemy_num = gameState->numOfEnemies; if (enemies == NULL) return; - while (enemy_num--) - { + while (enemy_num--) { char *frames[4]; - if (enemies->type == E_CIVILIAN) - { + if (enemies->type == E_CIVILIAN) { frames[0] = "vampire_1"; frames[1] = "vampire_2"; frames[2] = "vampire_3"; frames[3] = "vampire_4"; - } - else if (enemies->type == E_FARMER) - { + } else if (enemies->type == E_FARMER) { frames[0] = "slime_1_0"; frames[1] = "slime_1_1"; frames[2] = "slime_1_2"; @@ -102,20 +43,10 @@ static void drawEnemies(Game_System *game) SpriteAnimation idle = createSpriteAnimation(4, frames, 6, true); - Vector2 pos = enemies->position; + Vector2 pos = enemies->object.rigidBody.position; bool flip = (enemies->drawDirection == -1) ? true : false; - if (enemies->isMoving) - { - // TODO: Change this to the correct animation - // drawSpriteAnimationPro(&walk, (Rectangle){pos.x, pos.y, 64, 64}, - // (Vector2){0, 0}, 0, WHITE, flip); - } - else - { - // printf("pos.x: %f, pos.y: %f\n", pos.x, pos.y); - drawSpriteAnimationPro(&idle, (Rectangle){pos.x, pos.y, 64, 64}, - (Vector2){0, 0}, 0, WHITE, flip); - } + drawSpriteAnimationPro(&idle, (Rectangle){pos.x, pos.y, 64, 64}, + (Vector2){0, 0}, 0, WHITE, flip); disposeSpriteAnimation(&idle); enemies++; @@ -124,11 +55,47 @@ static void drawEnemies(Game_System *game) // disposeSpriteAnimation(&walk); } -static void drawBullets(Game_System *game) -{ +static bool checkCollision(Rectangle rect1, Rectangle rect2) { + // collision x-axis? + bool collisionX = + rect1.x + rect1.width >= rect2.x && rect2.x + rect2.width >= rect1.x; + // collision y-axis? + bool collisionY = + rect1.y + rect1.height >= rect2.y && rect2.y + rect2.height >= rect1.y; + // collision only if on both axes + return collisionX && collisionY; +} + +static int bulletCollision(CombatAction *combatActions) { + Bullet *bullet = &combatActions->action.bullet; + for (int j = 0; j < gameState->numOfEnemies; j++) { + Enemy *enemy = &gameState->enemies[j]; + + if (checkCollision((Rectangle){bullet->bulletInfo.rigidBody.position.x, + bullet->bulletInfo.rigidBody.position.y, + bullet->bulletInfo.collider.width, + bullet->bulletInfo.collider.height}, + (Rectangle){enemy->object.rigidBody.position.x, + enemy->object.rigidBody.position.y, + enemy->object.collider.width, + enemy->object.collider.height})) { + *combatActions = + gameState->combatActions[gameState->numOfCombatActions - 1]; + gameState->numOfCombatActions--; + enemy->stats.health.currentHealth -= bullet->bulletInfo.bulletDamage; + if (enemy->stats.health.currentHealth <= 0) { + gameState->enemies[j] = gameState->enemies[gameState->numOfEnemies - 1]; + gameState->numOfEnemies--; + } + return 1; + } + } + return 0; +} +static void drawBullets() { int x = 320, y = 96; - int bulletNum = game->num_of_bullets; - Bullet *bullets = game->bullets; + int actions = gameState->numOfCombatActions; + CombatAction *combatActions = gameState->combatActions; SpriteAnimation fireAnime = createSpriteAnimation(6, (char *[]){ @@ -141,65 +108,30 @@ static void drawBullets(Game_System *game) }, 12, true); - while (bulletNum--) - { - Vector2 pos = bullets->position; - Rectangle dest = {pos.x, pos.y, 32, 32}; - drawSpriteAnimationPro(&fireAnime, dest, (Vector2){0, 0}, 0, WHITE, false); - bullets->position.x += bullets->bulletSpeed * cos(bullets->angle * DEG2RAD); - bullets->position.y += bullets->bulletSpeed * sin(bullets->angle * DEG2RAD); - bullets++; - } - - disposeSpriteAnimation(&fireAnime); -} -static bool checkCollision(Rectangle rect1, Rectangle rect2) -{ - // collision x-axis? - bool collisionX = rect1.x + rect1.width >= rect2.x && - rect2.x + rect2.width >= rect1.x; - // collision y-axis? - bool collisionY = rect1.y + rect1.height >= rect2.y && - rect2.y + rect2.height >= rect1.y; - // collision only if on both axes - return collisionX && collisionY; -} - -static void bulletCollision(Game_System *game) -{ - for (int i = 0; i < game->num_of_bullets; i++) - { - for (int j = 0; j < game->num_of_enemies; j++) - { - if (checkCollision((Rectangle){game->bullets[i].position.x, game->bullets[i].position.y, game->bullets[i].body.width, game->bullets[i].body.height}, - (Rectangle){game->enemies[j].position.x, game->enemies[j].position.y, game->enemies[j].body.width, game->enemies[j].body.height})) - { - game->bullets[i] = game->bullets[game->num_of_bullets - 1]; - game->num_of_bullets--; - - if (game->enemies[j].health > 0) - game->enemies[j].health -= game->bullets[i].bulletDamage; - else - game->enemies[j].health = 0; - - // WTF???? Who wrote this shit?? - if (game->enemies[j].health == 0) - { - game->enemies[j] = game->enemies[game->num_of_enemies - 1]; - game->num_of_enemies--; - } - } + while (actions--) { + if (combatActions->type == ACTION_BULLET) { + Bullet *bullets = &combatActions->action.bullet; + Vector2 *pos = &bullets->bulletInfo.rigidBody.position; + Rectangle dest = {pos->x, pos->y, bullets->bulletInfo.collider.width, + bullets->bulletInfo.collider.height}; + drawSpriteAnimationPro(&fireAnime, dest, (Vector2){0, 0}, 0, WHITE, + false); + pos->x += + bullets->bulletInfo.bulletSpeed * cos(combatActions->angle * DEG2RAD); + pos->y += + bullets->bulletInfo.bulletSpeed * sin(combatActions->angle * DEG2RAD); + combatActions -= bulletCollision(combatActions); } + combatActions++; } + disposeSpriteAnimation(&fireAnime); } -void drawScene() -{ - Game_System *gameSystemInstance = getGameSystemInstance(); +void drawScene() { BeginDrawing(); ClearBackground(GetColor(0x052c46ff)); - drawPlayers(gameSystemInstance); + drawPlayers(); // TODO: Delete Me later // Example for using atlas @@ -207,11 +139,9 @@ void drawScene() // 0, WHITE, false); // TODO: Delete Me later - bulletCollision(gameSystemInstance); - - drawEnemies(gameSystemInstance); + drawEnemies(); - drawBullets(gameSystemInstance); + drawBullets(); EndDrawing(); } diff --git a/src/system/init.c b/src/system/init.c index f539a33..a683ca6 100644 --- a/src/system/init.c +++ b/src/system/init.c @@ -14,17 +14,21 @@ static void initCharacterDictionary() { // Add in asc order // Because we fetch the info using binary search. dict[0].opcode = CAT; - dict[0].entry.character = (GameObject){ - .rigidBody = {.velocity = (Vector2){5, 5}, + dict[0].entry.player = (Player){ + .type = CAT, + .stats = {.health = {.maxHealth = 100, .currentHealth = 100}, + .attack = {.power = 1.0f, .cooldown = 5, .speed = 1.0f}, + .defense = {.value = 3, .nearHitValue = 6}, + .speed = 5}, + .object = { + .rigidBody = {.velocity = (Vector2){0, 0}, .acceleration = (Vector2){0, 0}, 1.0, false}, .collider = {.offset = (Vector2){0, 0}, 32, 32}, - .spriteRenderer = {.texture = LoadTexture("./src/"), 32, 32}, + .spriteRenderer = {}, .animator = {}, - .stats = {.health = {.maxHealth = 100, .currentHealth = 100}, - .attack = {.power = 1.0f, .cooldown = 5, .speed = 1.0f}, - .defense = {.value = 3, .nearHitValue = 6}}}; + }}; dict[1].opcode = WEREWOLF; dict[2].opcode = PYROMANIAC; @@ -47,23 +51,47 @@ static void initEnemyDictionary() { dict[0].entry.enemy = (Enemy){ .name = "Civilian", .object = - {.rigidBody = {.velocity = (Vector2){5, 5}, - .acceleration = (Vector2){0, 0}, - 1.0, - false}, - .collider = {.offset = (Vector2){0, 0}, 32, 32}, - .spriteRenderer = {.texture = LoadTexture("./src/"), 32, 32}, - .animator = {}, - .stats = {.health = {.maxHealth = 100, .currentHealth = 100}, - .attack = {.power = 1.0f, .cooldown = 5, .speed = 1.0f}, - .defense = {.value = 3, .nearHitValue = 6}}}, + { + .rigidBody = {.velocity = (Vector2){5, 5}, + .acceleration = (Vector2){0, 0}, + 1.0, + false}, + .collider = {.offset = (Vector2){0, 0}, 32, 32}, + .spriteRenderer = {}, + .animator = {0}, + }, .ai = {.detectionRange = 100, .attackCooldown = 3, .dodgePercentage = 0, .speed = 2, - .state = FLEE}}; + .state = FLEE}, + .stats = {.health = {.maxHealth = 100, .currentHealth = 100}, + .attack = {.power = 1.0f, .cooldown = 5, .speed = 1.0f}, + .defense = {.value = 3, .nearHitValue = 6}}, + }; dict[1].opcode = E_FARMER; + dict[1].entry.enemy = (Enemy){ + .name = "Farmer", + .object = + { + .rigidBody = {.velocity = (Vector2){5, 5}, + .acceleration = (Vector2){0, 0}, + 1.0, + false}, + .collider = {.offset = (Vector2){0, 0}, 32, 32}, + .spriteRenderer = {}, + .animator = {}, + }, + .ai = {.detectionRange = 100, + .attackCooldown = 3, + .dodgePercentage = 0, + .speed = 2, + .state = FLEE}, + .stats = {.health = {.maxHealth = 100, .currentHealth = 100}, + .attack = {.power = 1.0f, .cooldown = 5, .speed = 1.0f}, + .defense = {.value = 3, .nearHitValue = 6}}}; + dict[2].opcode = E_KNIGHT; gameState->enemyDictionary = dict; @@ -81,11 +109,11 @@ static void initPlayerWeaponDictionary() { // Because we fetch the info using binary search. dict[0].opcode = P_GUN; dict[0].entry.weapon = (Weapon){ - RANGED_WEAPON, + .type = RANGED_WEAPON, .weapon.ranged = { - .stats = {10, 0.5, 0, .weaponSprite = {LoadTexture("./src/"), 5, 5}}, + .stats = {10, 0.5, 0, .weaponSprite = {}}, .bulletInfo = {3, 10, 100, 10, - .bulletSprite = {LoadTexture("./src/"), 5, 5}}, + .bulletSprite = {}}, 30, 30}}; @@ -104,36 +132,16 @@ static void initEnemyWeaponDictionary() { // Because we fetch the info using binary search. dict[0].opcode = E_SWORD; dict[0].entry.weapon = (Weapon){ - MELEE_WEAPON, + .type = MELEE_WEAPON, + .weapon.melee = { - .stats = {10, 0.5, 0, .weaponSprite = {LoadTexture("./src/"), 5, 5}}, + .stats = {10, 0.5, 0, .weaponSprite = {}}, .slashInfo = {3, 10, true, - .slashSprite = {LoadTexture("./src/"), 5, 5}}}}; - + .slashSprite = {}}}}; + gameState->enemyWeaponDictionary = dict; } -static Weapon initWeapon(int opcode, bool isPlayer) { - Dictionary *dict = (isPlayer ? gameState->playerWeaponDictionary - : gameState->enemyWeaponDictionary); - int l = 0, r = (isPlayer ? NUM_OF_P_WEAPON : NUM_OF_E_WEAPON) - 1; - - while (l <= r) { - int mid = l + (r - l) / 2; - int cmp = dict[mid].opcode - opcode; - if (!cmp) { - return dict[mid].entry.weapon; - } - if (cmp < 0) - l = mid + 1; - else - r = mid - 1; - } - - fprintf(stderr, "Error: Player Weapon Dictionary Failed"); - exit(EXIT_FAILURE); -} - //======================================================== // Init Functions //======================================================== @@ -160,26 +168,32 @@ GameState *initGameState() { gameSystemInstance->isGameOver = false; gameSystemInstance->isFinished = false; gameSystemInstance->atlasImages = NULL; - initSettings(gameSystemInstance); - initPlayerWeaponDictionary(); + + gameSystemInstance->characterDictionary = NULL; + gameSystemInstance->enemyDictionary = NULL; + gameSystemInstance->playerWeaponDictionary = NULL; + gameSystemInstance->enemyWeaponDictionary = NULL; } return gameSystemInstance; } +void initDictionary() { + initCharacterDictionary(); + initEnemyDictionary(); + initPlayerWeaponDictionary(); + initEnemyWeaponDictionary(); +} + void initSettings() { gameState->settings.screenWidth = SCREEN_WIDTH; gameState->settings.screenHeight = SCREEN_HEIGHT; gameState->settings.musicVolume = 50; + gameState->settings.soundVolume = 50; gameState->settings.music_on = true; gameState->settings.sfx_on = true; } -static void addPlayer(Player *player) { - Player *players = gameState->players; - players[gameState->numOfPlayers++] = *player; -} - static void addEnemy(Enemy *enemy) { Enemy *enemies = gameState->enemies; enemies[gameState->numOfEnemies++] = *enemy; @@ -208,45 +222,11 @@ Bullet *initBullet(int ID, BulletInfo *bulletInfo, Vector2 src, Vector2 dest) { bullet->bulletInfo = *bulletInfo; bullet->startPosition = src; bullet->transform = (CTransform){src, 0, (Vector2){1, 1}}; + bullet->bulletInfo.rigidBody.position = src; addBullet(bullet, GetAngleBetweenPoints(src, dest)); return bullet; } -Player *initPlayer(const char *name, P_TYPE type, P_WEAPON weapon, - Vector2 position, int ID) { - Settings settings = gameState->settings; - Dictionary *dict = gameState->characterDictionary; - Player *player = (Player *)malloc(sizeof(Player)); - int l = 0, r = NUM_OF_E_TYPE - 1; - - while (l <= r) { - int mid = l + (r - l) / 2; - int cmp = dict[mid].opcode - type; - if (!cmp) { - player->object = dict[mid].entry.character; - break; - } - if (cmp < 0) - l = mid + 1; - else - r = mid - 1; - } - player->name = strdup(name); - player->ID = ID; - player->type = type; - player->object.weapon = initWeapon(weapon, true); - player->object.transform.position = position; - player->score = 0; - player->drawDirection = 1; - player->direction = RIGHT; - player->fire = 0; - player->ID = ID; - player->experience = (Experience){.xp = 0, .level = 0}; - // TODO: Make dictionary for infos related to each type of character. - addPlayer(player); - return player; -} - Enemy *initEnemy(E_TYPE type, E_WEAPON weapon, Vector2 position) { Dictionary *dict = gameState->enemyDictionary; Enemy *enemy = (Enemy *)malloc(sizeof(Enemy)); @@ -265,8 +245,9 @@ Enemy *initEnemy(E_TYPE type, E_WEAPON weapon, Vector2 position) { else r = mid - 1; } - enemy->object.transform.position = position; - enemy->object.weapon = initWeapon(weapon, false); + enemy->type = type; + enemy->object.rigidBody.position = position; + enemy->weapon = initWeapon(weapon, false); addEnemy(enemy); return enemy; } diff --git a/src/system/init.h b/src/system/init.h index 5277a4c..914d0b5 100644 --- a/src/system/init.h +++ b/src/system/init.h @@ -5,6 +5,9 @@ #include "../CRougeLite.h" // NOTE: declare global extern vars #include "atlas.h" #include +// TODO: DELETE AFTER ENEMY REFACTOR +#include "../game/weapon.h" +void initSettings(); #endif // INIT_H diff --git a/src/system/input.c b/src/system/input.c index f5d15d1..be62ebb 100644 --- a/src/system/input.c +++ b/src/system/input.c @@ -1,104 +1,48 @@ #include "input.h" #include "../CRougeLite.h" +#include -static void keyboardEventHandler(GameState *game); -static void mouseEventHandler(GameState *game); +static void mouseEventHandler(); -void handleInput() { - GameState *gameSystemInstance = getGameSystemInstance(); - keyboardEventHandler(gameSystemInstance); - - mouseEventHandler(gameSystemInstance); -} - -// 1,2,5,9 theses number if you sum any two numbers you will get a unique number not in the list and not equal the sum of any other two numbers same for 3 numbers. -// I choose these numbers to make it easy to know the angle of movement without making alot of conditions on key press. -// 1,2,5,9 -// w,a,s,d - -static void keyboardEventHandler() -{ - GameState *game = gameState; - int selected_player = 0; - Player *player = ((game->players) + selected_player); - Vector2 *pos = &(player->position); - DIRECTIONS *direction = &(player->direction); - int speed = player->speed; - - static int angles[18]; // the size is the sum of the 4 numbers for fast retrieval of the angle. - memset(angles, -1, sizeof(angles)); - angles[W + D + A] = angles[W] = -90; - angles[W + S + A] = angles[A] = 180; - angles[A + S + D] = angles[S] = 90; - angles[W + S + D] = angles[D] = 0; - angles[W + A] = -135; - angles[W + D] = -45; - angles[S + D] = 45; - angles[S + A] = 135; - - player->isMoving = false; - - int sum = 0; - - if (IsKeyDown(KEY_ESCAPE)) - { - game->finished = true; - } - if (IsKeyDown(KEY_W)) - { - sum += 1; - *direction = UP; - } - if (IsKeyDown(KEY_S)) - { - sum += 5; - *direction = DOWN; - } - if (IsKeyDown(KEY_A)) - { - sum += 2; - *direction = LEFT; - player->drawDirection = -1; - } - if (IsKeyDown(KEY_D)) - { - sum += 9; - *direction = RIGHT; - player->drawDirection = 1; - } - if (angles[sum] == -1) - return; - player->isMoving = true; - pos->x += speed * cos(angles[sum] * DEG2RAD); - pos->y += speed * sin(angles[sum] * DEG2RAD); +void handleInput() { + mouseEventHandler(); } -static void mouseEventHandler() -{ +static void mouseEventHandler() { int selected_player = 0; - Player *player = ((game->players) + selected_player); + Player *player = ((gameState->players) + selected_player); - Vector2 srcPos = {(double)player->position.x + player->body.width / 2 - 16, (double)player->position.y + player->body.height / 2 - 16}; + Vector2 srcPos = {(double)player->object.rigidBody.position.x + + player->object.collider.width / 2 - 16, + (double)player->object.rigidBody.position.y + + player->object.collider.height / 2 - 16}; Vector2 mousePos = GetMousePosition(); mousePos.x -= 16; - mousePos.y -= 16; + mousePos.y -= 16; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) - { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { player->fire = 1; } - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) - { + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { player->fire = 0; } float deltaTime = GetFrameTime(); // Get time in seconds for one frame - if (player->fire == 1 && player->reloadTime <= 0.0f) - { - initBullet(player->weapon, selected_player, (RigidBody2d){16, 16}, srcPos, mousePos); - player->reloadTime = player->fireRate; + float *reloadTime = &(player->inventory.weapons->weapon.ranged.stats.lastUseTime); + + float cooldown = player->inventory.weapons->weapon.ranged.stats.cooldown; + + int *ammo = &(player->inventory.weapons->weapon.ranged.ammo); + + if (player->fire == 1 && *ammo > 0 && player->inventory.weapons->type == RANGED_WEAPON && + *reloadTime <= 0.0f) { + + initBullet(player->ID, &(player->inventory.weapons->weapon.ranged.bulletInfo), srcPos, + mousePos); + *ammo -= 1; + *reloadTime = cooldown; } - if (player->reloadTime > 0.0f) - player->reloadTime -= deltaTime; + if (*reloadTime > 0.0f) + *reloadTime -= deltaTime; }