Groundwork for enemy entity type

dev
Shariq Shah 6 years ago
parent 5ffa594200
commit c3305a9522
  1. 4
      assets/scenes/Level_1.symtres
  2. 2
      src/common/version.h
  3. 49
      src/game/editor.c
  4. 124
      src/game/enemy.c
  5. 16
      src/game/enemy.h
  6. 24
      src/game/entity.c
  7. 28
      src/game/entity.h
  8. 1
      src/game/player.c
  9. 90
      src/game/scene.c
  10. 7
      src/game/scene.h
  11. 8
      todo.txt

@ -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

@ -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

@ -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
{

@ -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 <string.h>
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;
}
}

@ -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

@ -18,6 +18,7 @@
#include "scene.h"
#include "game.h"
#include "texture.h"
#include "enemy.h"
#include <stdlib.h>
#include <string.h>
@ -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;

@ -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);

@ -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};

@ -18,6 +18,7 @@
#include "../common/hashmap.h"
#include "renderer.h"
#include "sound_source.h"
#include "enemy.h"
#include <assert.h>
#include <string.h>
@ -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);

@ -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

@ -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
@ -396,3 +401,4 @@ Done:
* 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
* Sound source entity functions that automatically track if handles are valid and create/update as necessary
Loading…
Cancel
Save