From 05451c6ca3db847eed27a7ec454ea98f7f73be39 Mon Sep 17 00:00:00 2001 From: Sara Date: Thu, 25 Jan 2024 00:07:34 +0100 Subject: [PATCH] feat: implemented level functions and refactored Enemy and Player to spawn using level files --- core/src/level.c | 188 ++++++++++++++++++++++++++++++++++++++++++++ core/src/level.h | 18 +++++ core/src/program.c | 3 + game/src/BeatEmUp.c | 6 +- game/src/Enemy.c | 14 ++-- game/src/Enemy.h | 3 +- game/src/Player.c | 15 ++-- game/src/Player.h | 3 +- 8 files changed, 231 insertions(+), 19 deletions(-) create mode 100644 core/src/level.c create mode 100644 core/src/level.h diff --git a/core/src/level.c b/core/src/level.c new file mode 100644 index 0000000..83a20bb --- /dev/null +++ b/core/src/level.c @@ -0,0 +1,188 @@ +#include "level.h" +#include "game_world.h" +#include "physics_entity.h" +#include "physics_world.h" +#include "strutil.h" +#include "variant.h" +#include "ctype.h" + +static Dictionary internal_spawn_functions = { + .list = { .data = NULL, .len = 0, .cap = 0, .element_size = 0 }, + .element_size = 0 +}; + +static int is_open_bracket(int c) { return c == '{'; } +static int is_close_bracket(int c) { return c == '}'; } +static int is_colon(int c) { return c == ':'; } +static int ends_parameter(int c) { return c == '}' || c == ','; } + +int level_init() { + internal_spawn_functions = dictionary_from_type(LevelSpawnFn); + return 0; +} + +int level_clean() { + dictionary_empty(&internal_spawn_functions); + return 0; +} + +int level_register_spawner(const char* object_tag, LevelSpawnFn spawn_function) { + dictionary_set_value(LevelSpawnFn, &internal_spawn_functions, object_tag, spawn_function); + return 0; +} + +static inline +void spawn_object(LevelSpawnFn spawner, Dictionary* args) { + BehaviourEntity entity = spawner(args); + game_world_add_entity(entity); + if(TC_MIRRORS(entity, PhysicsEntity)) + physics_world_add_entity(TC_CAST(entity, PhysicsEntity)); +} + +static inline +long get_until(FILE* fp, char* out_buf, size_t buf_len, CharPredFn predfn) { + if(feof(fp)) return 0; + int c; + char* write = out_buf; + long count = 0; + while(c != EOF) { + // don't overflow + if(out_buf != NULL && count + 1 >= buf_len) + return count; + // fetch next character + c = fgetc(fp); + // check if this is the desired character + if(predfn(c)) { + if(write != NULL) { + *write = c; + if(count + 2 < buf_len) *(write + 1) = '\0'; + } + return count + 1; + } + // skip space characters (unless search is a space character, which is handled above) + if(isspace(c)) continue; + // write and increment + if(write != NULL) { + *write = c; + ++write; + } + ++count; + } // EOF reached + // write null terminator in place of EOF if possible + if(write != NULL) + *(write-1) = '\0'; + return count + 1; +} + +static inline +long get_key(FILE* fp, char* out, size_t out_size) { + long length = get_until(fp, out, out_size, is_colon); + if(strfirst(out, out + length, '}') == 0) + return 0; + if(feof(fp)) + return -1; + length--; + out[length] = '\0'; + return length; +} + +static inline +Variant get_value(FILE* fp, char* buffer, size_t buffer_size, int* out_end_of_object) { + long length = 0; + *out_end_of_object = 0; + do { + length += get_until(fp, buffer+length, buffer_size-length, ends_parameter); + if (length <= 0) + return UndefinedVariant(); + *out_end_of_object = strfirst(buffer, buffer + length, '}') != -1; + } while(strcount(buffer, buffer+length, '(') != strcount(buffer, buffer+length, ')') && !(*out_end_of_object)); + length--; + buffer[length] = '\0'; + if(isdigit(buffer[0])) { + return NumberVariant(atof(buffer)); + } else if(buffer[0] == '"') { + size_t result_length = strfirst(buffer+1, buffer+length, '"'); + char* string = malloc(result_length); + strncpy(string, buffer+1, result_length-1); + string[result_length] = '\0'; + return StringVariant(string, result_length); + } else if(strncmp(buffer, "Vector(", 7) == 0) { + size_t comma_index = strfirst(buffer + 7, buffer + length, ','); + } + return UndefinedVariant(); +} + +static inline +int peek_next_non_blank(FILE* fp) { + long position = ftell(fp); + int c; + do { + c = fgetc(fp); + } while(isspace(c)); + fseek(fp, position, SEEK_SET); + return c; +} + +static inline +int load_args(FILE* fp, Dictionary* out, char* buffer, size_t buffer_size) { + char* key; + long length; + int is_end; + do { + length = get_key(fp, buffer, buffer_size); + if(length == -1) // detect EOF before end of key + return -1; + key = malloc(length+1); + strncpy(key, buffer, length); + Variant var = get_value(fp, buffer, buffer_size, &is_end); + dictionary_set_value(Variant, out, key, var); + free(key); + if(peek_next_non_blank(fp) == '}') { + get_until(fp, NULL, 0, ends_parameter); + break; + } + } while(!is_end); + return 0; +} + +int level_parse_file(FILE* fp) { + // initialize a buffer string + size_t length = 81; + char buffer[length]; + char* obj_tag = NULL; + Dictionary args = dictionary_new(sizeof(Variant)); + LevelSpawnFn spawner = NULL; + do { + // read the next line of the level file + length = get_until(fp, buffer, sizeof(buffer)-1, is_open_bracket); + --length; + buffer[length] = '\0'; + long start = strfirst(buffer, buffer+length, '}') + 1; + // initialize object tag buffer + obj_tag = malloc((length-start)+1); + obj_tag[length - start] = '\0'; + strncpy(obj_tag, buffer + start, length - start); + // find the spawn function + if(dictionary_try_get(&internal_spawn_functions, obj_tag, &spawner)) { + // load arguments from file and spawn object + spawn_object(spawner, &args); + load_args(fp, &args, buffer, sizeof(buffer)); + // clean up for next loop + free(obj_tag); + dictionary_empty(&args); + } else { + // clean up for next loop + free(obj_tag); + dictionary_empty(&args); + obj_tag = NULL; + // return -1; + } + } while(!feof(fp)); + return 0; +} + +int level_load_file(const char* path) { + FILE* fp = fopen("assets/test.sc", "r"); + level_parse_file(fp); + return 0; +} diff --git a/core/src/level.h b/core/src/level.h new file mode 100644 index 0000000..f677263 --- /dev/null +++ b/core/src/level.h @@ -0,0 +1,18 @@ +#ifndef _fencer_level_h +#define _fencer_level_h + +#include "stdio.h" +#include "behaviour_entity.h" +#include "dictionary.h" + +typedef BehaviourEntity(*LevelSpawnFn)(Dictionary* args); + +extern int level_init(); +extern int level_clean(); + +extern int level_register_spawner(const char* object_tag, LevelSpawnFn spawn_function); + +extern int level_parse_file(FILE* level); +extern int level_load_file(const char* path); + +#endif // !_fencer_level_h diff --git a/core/src/program.c b/core/src/program.c index c5717d9..fee1a71 100644 --- a/core/src/program.c +++ b/core/src/program.c @@ -2,6 +2,7 @@ #include "camera.h" #include "game_world.h" #include "physics_world.h" +#include "level.h" #include "time.h" #include "assets.h" #include "debug.h" @@ -67,6 +68,7 @@ void program_run(const struct ProgramSettings* settings) { assets_init(); input_init(); game_world_init(); + level_init(); LOG_INFO("settings->on_play"); settings->on_play(); @@ -99,6 +101,7 @@ void program_run(const struct ProgramSettings* settings) { } void program_quit() { + level_clean(); game_world_close(); input_clean(); assets_clean(); diff --git a/game/src/BeatEmUp.c b/game/src/BeatEmUp.c index 2bb000d..4bc2d39 100644 --- a/game/src/BeatEmUp.c +++ b/game/src/BeatEmUp.c @@ -1,11 +1,13 @@ #include "Player.h" #include "Enemy.h" +#include "level.h" #include "program.h" static void play() { - SpawnPlayer(ZeroVector); - SpawnEnemy(MakeVector(1.f, 0.0f), EnemyIdle()); + level_register_spawner("Player", SpawnPlayer); + level_register_spawner("Enemy", SpawnEnemy); + level_load_file("assets/test.sc"); } static diff --git a/game/src/Enemy.c b/game/src/Enemy.c index c06324c..9e0f9c0 100644 --- a/game/src/Enemy.c +++ b/game/src/Enemy.c @@ -6,6 +6,7 @@ #include "physics_world.h" #include "program.h" #include "sprite.h" +#include "variant.h" START_REFLECT(Enemy) REFLECT_TYPECLASS(Enemy, Transformable) @@ -77,19 +78,18 @@ Enemy* MakeEnemy() { self->idleAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Idle.png", IVectorFrom(512)), 1.5f, LoopMode_Loop); self->walkAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Walk.png", IVectorFrom(512)), 1.5f, LoopMode_Loop); self->hurtAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Hurt.png", IVectorFrom(512)), 5.f, LoopMode_Stop); + self->behaviour = state_machine_init(self, EnemyIdle()); return self; } -Enemy* SpawnEnemy(Vector location, const State* entryState) { +BehaviourEntity SpawnEnemy(Dictionary* args) { + Variant value; Enemy* self = MakeEnemy(); - self->behaviour = state_machine_init(self, entryState); // spawn at location - rigidbody_get_transform(self->rigidbody)->position = location; - // register into game world - game_world_add_entity(Enemy_as_BehaviourEntity(self)); - physics_world_add_entity(Enemy_as_PhysicsEntity(self)); - return self; + if(dictionary_try_get(args, "position", &value) && value.type == Variant_Vector) + rigidbody_get_transform(self->rigidbody)->position = value.as_vector; + return Enemy_as_BehaviourEntity(self); } void EnemyStart(Enemy* self) {} diff --git a/game/src/Enemy.h b/game/src/Enemy.h index 4e683dc..b81046c 100644 --- a/game/src/Enemy.h +++ b/game/src/Enemy.h @@ -1,6 +1,7 @@ #ifndef FIGHT_ENEMY_H #define FIGHT_ENEMY_H +#include "dictionary.h" #include "mirror.h" #include "transform.h" #include "state_machine.h" @@ -41,7 +42,7 @@ typedef struct Enemy { } Enemy; extern Enemy* MakeEnemy(); -extern Enemy* SpawnEnemy(Vector location, const State* entryState); +extern BehaviourEntity SpawnEnemy(Dictionary* args); extern void EnemyStart(Enemy* self); extern void EnemyUpdate(Enemy* self, float deltaTime); diff --git a/game/src/Player.c b/game/src/Player.c index eda4869..38f6842 100644 --- a/game/src/Player.c +++ b/game/src/Player.c @@ -1,12 +1,13 @@ #include "Player.h" #include "debug.h" -#include "game_world.h" +#include "dictionary.h" #include "input_axis.h" #include "physics_world.h" #include "PlayerStates.h" #include "Layers.h" #include "program.h" +#include "variant.h" const Vector PLAYER_SPEED = { 1.0f, 0.50f }; static const float PLAYER_INPUT_RATE = 1.f/15.f; @@ -131,15 +132,13 @@ Player* MakePlayer() { return self; } -Player* SpawnPlayer(Vector location) { +BehaviourEntity SpawnPlayer(Dictionary* args) { + Variant arg; Player* self = MakePlayer(); - self->transform.position = location; - - game_world_add_entity(Player_as_BehaviourEntity(self)); - physics_world_add_entity(Player_as_PhysicsEntity(self)); - Internal_PlayerInitInput(self); - return self; + if(dictionary_try_get(args, "position", &arg) && arg.type == Variant_Vector) + self->transform.position = arg.as_vector; + return Player_as_BehaviourEntity(self); } void DestroyPlayer(Player* self) { diff --git a/game/src/Player.h b/game/src/Player.h index 5d5cb48..61fe089 100644 --- a/game/src/Player.h +++ b/game/src/Player.h @@ -1,6 +1,7 @@ #ifndef FIGHT_PLAYER_H #define FIGHT_PLAYER_H +#include "dictionary.h" #include "state_machine.h" #include "mirror.h" #include "behaviour_entity.h" @@ -64,7 +65,7 @@ typedef struct Player { } Player; Player* MakePlayer(); -Player* SpawnPlayer(Vector location); +BehaviourEntity SpawnPlayer(Dictionary* args); void DestroyPlayer(Player* self); void PlayerStart(Player* self);