From c3305a9522c5aa741eae31114ca01db3774560bb Mon Sep 17 00:00:00 2001 From: Shariq Shah Date: Sat, 18 Jan 2020 21:59:23 +1100 Subject: [PATCH] Groundwork for enemy entity type --- assets/scenes/Level_1.symtres | 4 +- src/common/version.h | 2 +- src/game/editor.c | 49 +++++++++++--- src/game/enemy.c | 124 ++++++++++++++++++++++++++++++++++ src/game/enemy.h | 16 +++++ src/game/entity.c | 24 +++++-- src/game/entity.h | 28 +++++++- src/game/player.c | 1 - src/game/scene.c | 90 +++++++++++++++++++++++- src/game/scene.h | 7 +- todo.txt | 10 ++- 11 files changed, 330 insertions(+), 25 deletions(-) create mode 100644 src/game/enemy.c create mode 100644 src/game/enemy.h diff --git a/assets/scenes/Level_1.symtres b/assets/scenes/Level_1.symtres index b1c874c..b0dd756 100755 --- a/assets/scenes/Level_1.symtres +++ b/assets/scenes/Level_1.symtres @@ -16,9 +16,9 @@ Player { type : 2 scale : 1.000 1.000 1.000 - rotation : 0.000 -0.351 0.000 -0.938 + rotation : 0.000 0.028 0.000 -1.001 active : true - position : 1.750 2.869 22.751 + position : 0.144 2.974 27.020 bouding_box_min : -0.500 -0.500 -0.500 name : Player bouding_box_max : 0.500 0.500 0.500 diff --git a/src/common/version.h b/src/common/version.h index ddb39b6..62793b2 100755 --- a/src/common/version.h +++ b/src/common/version.h @@ -4,7 +4,7 @@ /* Auto generated version file. DO NOT MODIFY */ #define SYMMETRY_VERSION_MAJOR 0 #define SYMMETRY_VERSION_MINOR 1 -#define SYMMETRY_VERSION_REVISION 313 +#define SYMMETRY_VERSION_REVISION 314 #define SYMMETRY_VERSION_BRANCH "dev" #endif \ No newline at end of file diff --git a/src/game/editor.c b/src/game/editor.c index 8f7b016..73ce889 100755 --- a/src/game/editor.c +++ b/src/game/editor.c @@ -1690,14 +1690,6 @@ void editor_window_scene_hierarchy(struct nk_context* context, struct Editor* ed nk_layout_row_dynamic(context, 380, 1); if(nk_group_begin(context, "Entity Name", NK_WINDOW_SCROLL_AUTO_HIDE)) { - - if(nk_tree_push(context, NK_TREE_TAB, "Entities", NK_MAXIMIZED)) - { - for(int i = 0; i < MAX_ENTITIES; i++) - editor_show_entity_in_list(editor, context, scene, &scene->entities[i]); - nk_tree_pop(context); - } - if(nk_tree_push(context, NK_TREE_TAB, "Cameras", NK_MAXIMIZED)) { for(int i = 0; i < MAX_CAMERAS; i++) @@ -1726,6 +1718,20 @@ void editor_window_scene_hierarchy(struct nk_context* context, struct Editor* ed nk_tree_pop(context); } + if(nk_tree_push(context, NK_TREE_TAB, "Enemies", NK_MAXIMIZED)) + { + for(int i = 0; i < MAX_ENEMIES; i++) + editor_show_entity_in_list(editor, context, scene, &scene->enemies[i]); + nk_tree_pop(context); + } + + if(nk_tree_push(context, NK_TREE_TAB, "Entities", NK_MAXIMIZED)) + { + for(int i = 0; i < MAX_ENTITIES; i++) + editor_show_entity_in_list(editor, context, scene, &scene->entities[i]); + nk_tree_pop(context); + } + nk_group_end(context); } } @@ -2073,10 +2079,10 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* { if(strncmp(sound_source_filename_buffer, sound_source->source_buffer->filename, MAX_FILENAME_LEN) != 0) { - struct Sound_Source_Buffer* new_source_buffer = sound_source_create(sound, sound_source_filename_buffer, ST_WAV_STREAM); + struct Sound_Source_Buffer* new_source_buffer = sound_source_buffer_create(sound, sound_source_filename_buffer, ST_WAV_STREAM); if(new_source_buffer) { - sound_source_stop_all(sound, sound_source->source_buffer); + sound_source_buffer_stop_all(sound, sound_source->source_buffer); sound_source_instance_destroy(sound, sound_source->source_instance); sound_source->source_instance = sound_source_instance_create(sound, new_source_buffer, true); sound_source->source_buffer = new_source_buffer; @@ -2210,6 +2216,29 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* nk_tree_pop(context); } } + + + /* Enemy */ + if(entity->type == ET_ENEMY) + { + struct Enemy* enemy = (struct Enemy*)entity; + if(nk_tree_push(context, NK_TREE_TAB, "Enemy", NK_MAXIMIZED)) + { + nk_layout_row_dynamic(context, row_height, 1); + nk_property_int(context, "Damage", -INT_MAX, &enemy->damage, INT_MAX, 1, 1); + nk_property_int(context, "Health", 0, &enemy->health, INT_MAX, 1, 1); + + switch(enemy->type) + { + case ENEMY_TURRET: + { + nk_property_float(context, "Turn Speed", 0.f, &enemy->Turret.turn_speed, FLT_MAX, 0.5f, 0.1f); + } + break; + } + nk_tree_pop(context); + } + } } else { diff --git a/src/game/enemy.c b/src/game/enemy.c new file mode 100644 index 0000000..9d81088 --- /dev/null +++ b/src/game/enemy.c @@ -0,0 +1,124 @@ +#include "enemy.h" +#include "entity.h" +#include "scene.h" +#include "game.h" +#include "sound_source.h" +#include "../common/log.h" +#include "../common/hashmap.h" +#include "../common/parser.h" + +#include + +void enemy_init(struct Enemy* enemy, int type) +{ + struct Game_State* game_state = game_state_get(); + struct Scene* scene = game_state->scene; + + enemy->base.type = ET_ENEMY; + enemy->type = type; + + char weapon_name_buffer[MAX_ENTITY_NAME_LEN]; + char mesh_name_buffer[MAX_ENTITY_NAME_LEN]; + memset(weapon_name_buffer, '\0', MAX_ENTITY_NAME_LEN); + memset(mesh_name_buffer, '\0', MAX_ENTITY_NAME_LEN); + + snprintf(weapon_name_buffer, MAX_ENTITY_NAME_LEN, "%s_Weapon_Sound", enemy->base.name); + snprintf(mesh_name_buffer, MAX_ENTITY_NAME_LEN, "%s_Mesh", enemy->base.name); + + struct Sound_Source* weapon_sound = NULL; + struct Static_Mesh* mesh = NULL; + + /* Initialization specific to each enemy type */ + switch(enemy->type) + { + case ENEMY_TURRET: + { + enemy->Turret.turn_speed = 10.f; + enemy->health = 100; + enemy->damage = 10; + weapon_sound = scene_sound_source_create(scene, weapon_name_buffer, enemy, "sounds/bullet_1.wav", ST_WAV, false, false); + mesh = scene_static_mesh_create(scene, mesh_name_buffer, enemy, "suzanne.symbres", MAT_BLINN); + break; + } + default: + log_error("enemy:init", "Unsupported Enemy Type"); + break; + } + + enemy->weapon_sound = weapon_sound ? weapon_sound : NULL; + if(!weapon_sound) + log_error("enemy:init", "Failed to add weapon sound for %s", enemy->base.name); + + enemy->mesh = mesh ? mesh : NULL; + if(!mesh) + log_error("enemy:init", "Failed to add mesh from file for %s", enemy->base.name); + + enemy->mesh->base.flags |= EF_TRANSIENT; + enemy->weapon_sound->base.flags |= EF_TRANSIENT; +} + +void enemy_update(struct Enemy* enemy, struct Scene* scene, float dt) +{ + static float enemy_update_interval = 1.f / 2.f; + static float time_elapsed_since_last_update = 0.f; + + time_elapsed_since_last_update += dt; + if(time_elapsed_since_last_update < enemy_update_interval) + return; + + time_elapsed_since_last_update = 0.f; + struct Game_State* game_state = game_state_get(); + + log_message("Enemy_update"); + sound_source_play(game_state->sound, enemy->weapon_sound); +} + +void enemy_reset(struct Enemy* enemy) +{ + entity_reset(enemy, enemy->base.id); + enemy->base.flags = EF_NONE; +} + +struct Enemy* enemy_read(struct Parser_Object* object, const char* name, struct Entity* parent_entity) +{ + int enemy_type = -1; + struct Enemy* new_enemy = NULL; + struct Scene* scene = game_state_get()->scene; + + if(hashmap_value_exists(object->data, "enemy_type")) enemy_type = hashmap_int_get(object->data, "enemy_type"); + + if(enemy_type != -1) + { + new_enemy = scene_enemy_create(scene, name, parent_entity, enemy_type); + if(!new_enemy) + return new_enemy; + if(hashmap_value_exists(object->data, "health")) new_enemy->health = hashmap_int_get(object->data, "health"); + if(hashmap_value_exists(object->data, "damage")) new_enemy->damage = hashmap_int_get(object->data, "damage"); + + switch(new_enemy->type) + { + case ENEMY_TURRET: + { + if(hashmap_value_exists(object->data, "turn_speed")) new_enemy->Turret.turn_speed = hashmap_float_get(object->data, "turn_speed"); + } + break; + } + } + return new_enemy; +} + +void enemy_write(struct Enemy* enemy, struct Hashmap* entity_data) +{ + hashmap_int_set(entity_data, "enemy_type", enemy->type); + hashmap_int_set(entity_data, "health", enemy->health); + hashmap_int_set(entity_data, "damage", enemy->damage); + + switch(enemy->type) + { + case ENEMY_TURRET: + { + hashmap_float_set(entity_data, "turn_speed", enemy->Turret.turn_speed); + } + break; + } +} diff --git a/src/game/enemy.h b/src/game/enemy.h new file mode 100644 index 0000000..d54083e --- /dev/null +++ b/src/game/enemy.h @@ -0,0 +1,16 @@ +#ifndef ENEMY_H +#define ENEMY_H + +struct Enemy; +struct Scene; +struct Parser_Object; +struct Entity; +struct Hashmap; + +void enemy_init(struct Enemy* enemy, int type); +void enemy_update(struct Enemy* enemy, struct Scene* scene, float dt); +void enemy_reset(struct Enemy* enemy); +struct Enemy* enemy_read(struct Parser_Object* object, const char* name, struct Entity* parent_entity); +void enemy_write(struct Enemy* enemy, struct Hashmap* entity_data); + +#endif \ No newline at end of file diff --git a/src/game/entity.c b/src/game/entity.c index 40527fd..e637a0f 100755 --- a/src/game/entity.c +++ b/src/game/entity.c @@ -18,6 +18,7 @@ #include "scene.h" #include "game.h" #include "texture.h" +#include "enemy.h" #include #include @@ -202,8 +203,8 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ { hashmap_bool_set(entity_data, "has_fbo", true); } - break; } + break; case ET_STATIC_MESH: { struct Static_Mesh* mesh = (struct Static_Mesh*)entity; @@ -230,8 +231,8 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ }; hashmap_str_set(entity_data, "geometry", geom->filename); - break; } + break; case ET_LIGHT: { struct Light* light = (struct Light*)entity; @@ -246,8 +247,8 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ hashmap_bool_set(entity_data, "cast_shadow", light->cast_shadow); hashmap_bool_set(entity_data, "pcf_enabled", light->pcf_enabled); hashmap_vec3_set(entity_data, "color", &light->color); - break; } + break; case ET_SOUND_SOURCE: { struct Sound_Source* sound_source = (struct Sound_Source*)entity; @@ -260,8 +261,14 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ hashmap_float_set(entity_data, "sound_max_distance", sound_source->max_distance); hashmap_float_set(entity_data, "rolloff_factor", sound_source->rolloff_factor); hashmap_int_set(entity_data, "sound_attenuation_type", sound_source->attenuation_type); - break; } + break; + case ET_ENEMY: + { + struct Enemy* enemy = (struct Enemy*)entity; + enemy_write(enemy, entity_data); + } + break; }; return true; @@ -424,7 +431,7 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e { struct Sound* sound = game_state_get()->sound; - sound_source->source_buffer = sound_source_create(sound, hashmap_str_get(object->data, "source_filename"), sound_source->type); + sound_source->source_buffer = sound_source_buffer_create(sound, hashmap_str_get(object->data, "source_filename"), sound_source->type); if(sound_source->source_buffer) { sound_source->source_instance = sound_source_instance_create(sound, sound_source->source_buffer, true); @@ -496,6 +503,13 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e }; } break; + case ET_ENEMY: + { + new_entity = &enemy_read(object, name, parent_entity)->base; + if(!new_entity) + return new_entity; + } + break; default: log_warning("Unhandled Entity type '%d' detected", type); break; diff --git a/src/game/entity.h b/src/game/entity.h index 219c8ab..36d2923 100755 --- a/src/game/entity.h +++ b/src/game/entity.h @@ -27,6 +27,7 @@ enum Entity_Type ET_LIGHT, ET_STATIC_MESH, ET_SOUND_SOURCE, + ET_ENEMY, ET_MAX }; @@ -46,6 +47,12 @@ enum Camera_Type CAM_MAX }; +enum Enemy_Type +{ + ENEMY_TURRET = 0, + ENEMY_MAX +}; + enum Entity_Flags { EF_NONE = 0, @@ -67,7 +74,8 @@ enum Entity_Ray_Mask ERM_LIGHT = 1 << 3, ERM_STATIC_MESH = 1 << 4, ERM_SOUND_SOURCE = 1 << 5, - ERM_ALL = ERM_DEFAULT | ERM_PLAYER | ERM_CAMERA | ERM_LIGHT | ERM_STATIC_MESH | ERM_SOUND_SOURCE + ERM_ENEMY = 1 << 6, + ERM_ALL = ERM_DEFAULT | ERM_PLAYER | ERM_CAMERA | ERM_LIGHT | ERM_STATIC_MESH | ERM_SOUND_SOURCE | ERM_ENEMY }; struct Transform @@ -153,6 +161,7 @@ struct Light float depth_bias; }; + struct Collision { Rigidbody rigidbody; @@ -183,6 +192,23 @@ struct Player bool grounded; }; +struct Enemy +{ + struct Entity base; + int type; + int health; + int damage; + struct Static_Mesh* mesh; + struct Sound_Source* weapon_sound; + union + { + struct + { + float turn_speed; + }Turret; + }; +}; + void entity_init(struct Entity* entity, const char* name, struct Entity* parent); void entity_reset(struct Entity* entity, int id); bool entity_save(struct Entity* entity, const char* filename, int directory_type); diff --git a/src/game/player.c b/src/game/player.c index 2fa2778..a7303f1 100755 --- a/src/game/player.c +++ b/src/game/player.c @@ -61,7 +61,6 @@ void player_init(struct Player* player, struct Scene* scene) player->mesh->base.flags |= EF_TRANSIENT; player->weapon_sound->base.flags |= EF_TRANSIENT; - transform_parent_set(player_camera, player, true); vec3 cam_translation = {0.f, 1.5f, 0.f}; diff --git a/src/game/scene.c b/src/game/scene.c index 475fd46..bd851e5 100755 --- a/src/game/scene.c +++ b/src/game/scene.c @@ -18,6 +18,7 @@ #include "../common/hashmap.h" #include "renderer.h" #include "sound_source.h" +#include "enemy.h" #include #include @@ -33,6 +34,7 @@ void scene_init(struct Scene* scene) struct Game_State* game_state = game_state_get(); strncpy(scene->filename, "UNNAMED_SCENE", MAX_FILENAME_LEN); + //Initialize the root entity entity_init(&scene->root_entity, "ROOT_ENTITY", NULL); scene->root_entity.flags |= EF_ACTIVE; @@ -72,6 +74,12 @@ void scene_init(struct Scene* scene) for(int i = 0; i < MAX_ENTITY_ARCHETYPES; i++) memset(&scene->entity_archetypes[i][0], '\0', MAX_FILENAME_LEN); + for(int i = 0; i < MAX_ENEMIES; i++) + { + entity_reset(&scene->enemies[i], i); + scene->enemies->base.type = ET_ENEMY; + } + player_init(&scene->player, scene); editor_camera_init(game_state->editor, game_state->cvars); editor_init_entities(game_state->editor); @@ -283,6 +291,11 @@ void scene_write_entity_list(struct Scene* scene, int entity_type, struct Parser entity = &scene->sound_sources[0].base; stride = sizeof(struct Sound_Source); break; + case ET_ENEMY: + max_length = MAX_ENEMIES; + entity = &scene->enemies[0].base; + stride = sizeof(struct Enemy); + break; default: return; } @@ -330,6 +343,7 @@ void scene_destroy(struct Scene* scene) for(int i = 0; i < MAX_LIGHTS; i++) scene_light_remove(scene, &scene->lights[i]); for(int i = 0; i < MAX_STATIC_MESHES; i++) scene_static_mesh_remove(scene, &scene->static_meshes[i]); for(int i = 0; i < MAX_SOUND_SOURCES; i++) scene_sound_source_remove(scene, &scene->sound_sources[i]); + for(int i = 0; i < MAX_ENEMIES; i++) scene_enemy_remove(scene, &scene->enemies[i]); for(int i = 0; i < MAX_ENTITY_ARCHETYPES; i++) memset(&scene->entity_archetypes[i][0], '\0', MAX_FILENAME_LEN); player_destroy(&scene->player); entity_reset(&scene->root_entity, 0); @@ -338,7 +352,15 @@ void scene_destroy(struct Scene* scene) void scene_update(struct Scene* scene, float dt) { - if(game_state_get()->game_mode == GAME_MODE_GAME) player_update(&scene->player, scene, dt); + if(game_state_get()->game_mode == GAME_MODE_GAME) + { + player_update(&scene->player, scene, dt); + for(int i = 0; i < MAX_ENEMIES; i++) + { + if(scene->enemies[i].base.flags & EF_ACTIVE) + enemy_update(&scene->enemies[i], scene, dt); + } + } } void scene_post_update(struct Scene* scene) @@ -437,6 +459,19 @@ void scene_post_update(struct Scene* scene) if(light->base.transform.is_modified) light->base.transform.is_modified = false; } + for(int i = 0; i < MAX_ENEMIES; i++) + { + struct Enemy* enemy = &scene->enemies[i]; + if(!(enemy->base.flags & EF_ACTIVE)) continue; + + if(enemy->base.flags & EF_MARKED_FOR_DELETION) + { + scene_enemy_remove(scene, enemy); + continue; + } + + } + if(scene->player.base.transform.is_modified) { scene->player.base.transform.is_modified = false; @@ -580,7 +615,7 @@ struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char* new_sound_source->base.type = ET_SOUND_SOURCE; struct Entity* entity = &new_sound_source->base; - new_sound_source->source_buffer = sound_source_create(sound, filename, type); + new_sound_source->source_buffer = sound_source_buffer_create(sound, filename, type); if(!new_sound_source->source_buffer) { log_error("scene:sound_source_create", "Failed to load file '%s' to provide sound source for entity %s", filename, entity->name); @@ -618,6 +653,33 @@ struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char* return new_sound_source; } +struct Enemy* scene_enemy_create(struct Scene* scene, const char* name, struct Entity* parent, int type) +{ + assert(scene); + struct Enemy* new_enemy = NULL; + for(int i = 0; i < MAX_ENEMIES; i++) + { + struct Enemy* enemy = &scene->enemies[i]; + if(!(enemy->base.flags & EF_ACTIVE)) + { + new_enemy = enemy; + break; + } + } + + if(new_enemy) + { + entity_init(&new_enemy->base, name, parent ? parent : &scene->root_entity); + enemy_init(new_enemy, type); + } + else + { + log_error("scene:enemy_create", "Max enemy limit reached!"); + } + + return new_enemy; +} + void scene_entity_base_remove(struct Scene* scene, struct Entity* entity) { assert(scene && entity && entity->id >= 0); @@ -636,6 +698,13 @@ void scene_light_remove(struct Scene* scene, struct Light* light) light->valid = false; } +void scene_enemy_remove(struct Scene* scene, struct Enemy* enemy) +{ + assert(scene && enemy); + enemy_reset(enemy); + scene_entity_base_remove(scene, enemy); +} + void scene_camera_remove(struct Scene* scene, struct Camera* camera) { assert(scene && camera); @@ -749,6 +818,23 @@ struct Sound_Source* scene_sound_source_find(struct Scene* scene, const char* na return sound_source; } +struct Enemy* scene_enemy_get(struct Scene* scene, const char* name) +{ + assert(scene && name); + struct Enemy* enemy = NULL; + + for(int i = 0; i < MAX_ENEMIES; i++) + { + if(strncmp(name, scene->enemies[i].base.name, MAX_ENTITY_NAME_LEN) == 0) + { + enemy = &scene->enemies[i]; + break; + } + } + + return enemy; +} + struct Entity* scene_base_entity_get(struct Scene* scene, int id, int type) { assert(scene && id != -1 && type < ET_MAX); diff --git a/src/game/scene.h b/src/game/scene.h index cef4f88..66c1451 100755 --- a/src/game/scene.h +++ b/src/game/scene.h @@ -4,12 +4,13 @@ #include "entity.h" #include "renderer.h" -#define MAX_ENTITIES 1024 +#define MAX_ENTITIES 32 #define MAX_LIGHTS 30 #define MAX_CAMERAS 2 #define MAX_STATIC_MESHES 1024 #define MAX_SOUND_SOURCES 128 #define MAX_ENTITY_ARCHETYPES 32 +#define MAX_ENEMIES 64 struct Ray; struct Raycast_Result; @@ -24,6 +25,7 @@ struct Scene struct Camera cameras[MAX_CAMERAS]; struct Light lights[MAX_LIGHTS]; struct Sound_Source sound_sources[MAX_SOUND_SOURCES]; + struct Enemy enemies[MAX_ENEMIES]; char entity_archetypes[MAX_ENTITY_ARCHETYPES][MAX_FILENAME_LEN]; int active_camera_index; }; @@ -41,12 +43,14 @@ struct Light* scene_light_create(struct Scene* scene, const char* name, s struct Camera* scene_camera_create(struct Scene* scene, const char* name, struct Entity* parent, int width, int height); struct Static_Mesh* scene_static_mesh_create(struct Scene* scene, const char* name, struct Entity* parent, const char* geometry_name, int material_type); struct Sound_Source* scene_sound_source_create(struct Scene* scene, const char* name, struct Entity* parent, const char* filename, int type, bool loop, bool play); +struct Enemy* scene_enemy_create(struct Scene* scene, const char* name, struct Entity* parent, int type); void scene_entity_base_remove(struct Scene* scene, struct Entity* entity); void scene_light_remove(struct Scene* scene, struct Light* light); void scene_camera_remove(struct Scene* scene, struct Camera* camera); void scene_static_mesh_remove(struct Scene* scene, struct Static_Mesh* mesh); void scene_sound_source_remove(struct Scene* scene, struct Sound_Source* source); +void scene_enemy_remove(struct Scene* scene, struct Enemy* enemy); void* scene_find(struct Scene* scene, const char* name); // Looks in all entity type arrays and returns the first one found. Result should be cast back to expected type struct Entity* scene_entity_find(struct Scene* scene, const char* name); @@ -55,6 +59,7 @@ struct Camera* scene_camera_find(struct Scene* scene, const char* name); struct Static_Mesh* scene_static_mesh_find(struct Scene* scene, const char* name); struct Sound_Source* scene_sound_source_find(struct Scene* scene, const char* name); struct Entity* scene_base_entity_get(struct Scene* scene, int id, int type); +struct Enemy* scene_enemy_get(struct Scene* scene, const char* name); void scene_entity_parent_set(struct Scene* scene, struct Entity* entity, struct Entity* parent); void scene_entity_parent_reset(struct Scene* scene, struct Entity* entity); // Sets root entity as parent diff --git a/todo.txt b/todo.txt index 1d95b25..0f02bb1 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,12 @@ Todo: - - Sound source entity functions that automatically track if handles are valid and create/update as necessary + - Imlement reading/writing enemy mesh and weapon sound to file and resetting it in code + - Apply sound source properties to source instance whenever a new instance is created + - Enemy ray casting and shooting + - Player shooting - Player jump cooldown, don't allow jump until a certian time interval has passed, even if we're grounded - Sky Cube maps + - Scrolling textures + - Apply the selected entity's transformation when duplicating an entity that has an entity archetype - Gameplay level features: - Each scene should always have a directional light that serves as the main source of light when there are no other lights - Each scene should always have a texture cube that serves as the sky @@ -395,4 +400,5 @@ Done: * Improve player collision by impelenting sliding along collision plane in case of collision * Brought back sprinting by fixing a bug where player movement related variables were written to file as floats but read back as ints * Move player movement related variables from function to player struct and load them from config file - * Screen mouse coordinates to world-coordinates for aiming \ No newline at end of file + * Screen mouse coordinates to world-coordinates for aiming + * Sound source entity functions that automatically track if handles are valid and create/update as necessary \ No newline at end of file