feat: implemented level functions
and refactored Enemy and Player to spawn using level files
This commit is contained in:
parent
6035de1ad4
commit
05451c6ca3
188
core/src/level.c
Normal file
188
core/src/level.c
Normal file
|
@ -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;
|
||||
}
|
18
core/src/level.h
Normal file
18
core/src/level.h
Normal file
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue