From ac16c151a3d8a4f7bcf8d4f2b71ce9ed3316dceb Mon Sep 17 00:00:00 2001 From: Shariq Shah Date: Thu, 6 Feb 2020 20:11:19 +1100 Subject: [PATCH] Started implementing trigger entity type --- assets/entities/cube.symtres | 10 +++- assets/entities/cube_uv.symtres | 6 +- assets/entities/turret.symtres | 2 +- assets/models/cube.symbres | Bin 1312 -> 1312 bytes assets/scenes/scene_1.symtres | 63 ++++++++------------ src/common/limits.h | 1 + src/common/version.h | 2 +- src/game/editor.c | 44 +++++++++++++- src/game/entity.c | 22 +++++++ src/game/entity.h | 30 +++++++++- src/game/scene.c | 82 +++++++++++++++++++++++++ src/game/scene.h | 4 ++ src/game/trigger.c | 102 ++++++++++++++++++++++++++++++++ src/game/trigger.h | 10 ++++ todo.txt | 10 ++-- 15 files changed, 335 insertions(+), 53 deletions(-) create mode 100644 src/game/trigger.c create mode 100644 src/game/trigger.h diff --git a/assets/entities/cube.symtres b/assets/entities/cube.symtres index 45cdd0a..1a894e5 100644 --- a/assets/entities/cube.symtres +++ b/assets/entities/cube.symtres @@ -1,14 +1,18 @@ - Entity { type : 6 + scale : 135.000 1.000 135.000 material : 0 + rotation : 0.000 0.000 0.000 1.000 diffuse_color : 1.000 1.000 1.000 1.000 geometry : cube.symbres specular : 1.0000 active : true diffuse_texture : default.tga diffuse : 1.0000 + position : -17.000 0.000 -20.000 specular_strength : 1.0000 - name : Cube -} \ No newline at end of file + name : Ground + uv_scale : 7.700 8.900 +} + diff --git a/assets/entities/cube_uv.symtres b/assets/entities/cube_uv.symtres index f7b5653..f80eaeb 100755 --- a/assets/entities/cube_uv.symtres +++ b/assets/entities/cube_uv.symtres @@ -1,7 +1,7 @@ Entity { type : 6 - scale : 68.000 3.000 1.000 + scale : 135.000 5.000 1.000 material : 0 rotation : 0.000 0.000 0.000 1.000 diffuse_color : 1.000 1.000 1.000 1.000 @@ -10,9 +10,9 @@ Entity active : true diffuse_texture : default.tga diffuse : 1.0000 - position : -17.000 4.000 -87.000 + position : -17.000 3.000 -87.000 specular_strength : 1.0000 name : Cube - uv_scale : 15.000 1.000 + uv_scale : 5.000 63.500 } diff --git a/assets/entities/turret.symtres b/assets/entities/turret.symtres index bd73074..bd07cae 100644 --- a/assets/entities/turret.symtres +++ b/assets/entities/turret.symtres @@ -5,7 +5,7 @@ Entity pulsate_height : 1.5000 vision_range : 15.0000 pulsate_speed_scale : 0.1000 - rotation : 0.000 0.001 0.000 1.001 + rotation : 0.000 0.000 0.000 1.000 health : 116 alert_cooldown : 1.0000 color_alert : 1.000 1.000 0.000 1.000 diff --git a/assets/models/cube.symbres b/assets/models/cube.symbres index 7306f1600f8f2dca58289b5a983df45267045f43..b556980e22b7e96032c385405d3dfdccbed31552 100755 GIT binary patch literal 1312 zcmaKrhiwBf5CrL*bI$M3gah;{#igh+$ql$6CwYeA8T4DQfWeW=y*=^)-KCW7Dp;l& zn1wl*hXq)KC0K?PScNrMhYi?-E!c(~*o8gVhXXi-BRGZ=IE6DfhYPrbD`?;vZr~O^ z$K&2Rd;sgA`!_0@^uGS;`iJOxvya~Fwb!-PGjjC)`0Z17R;_pZIVvgh z1^m?ST>XF9Td(H0izUNi0YR^AC`|lp*%dc5}>AFL@ IW}OdvKLX=@!Tmax_distance, 32, false, abs_pos, rot, editor->selected_entity_color, 5); } break; + case ET_TRIGGER: + { + struct Trigger* trigger = (struct Trigger*)editor->selected_entity; + vec3 extents = { 0.f }; + vec3_sub(&extents, &trigger->base.derived_bounding_box.max, &trigger->base.derived_bounding_box.min); + im_box(extents.x, extents.y, extents.z, abs_pos, abs_rot, (vec4) { 1.f, 0.f, 0.f, 0.5f }, GDM_TRIANGLES, 5); + } + break; } /* Draw bounding box for selected entity */ @@ -1732,6 +1740,13 @@ void editor_window_scene_hierarchy(struct nk_context* context, struct Editor* ed nk_tree_pop(context); } + if(nk_tree_push(context, NK_TREE_TAB, "Triggers", NK_MAXIMIZED)) + { + for(int i = 0; i < MAX_SCENE_TRIGGERS; i++) + editor_show_entity_in_list(editor, context, scene, &scene->triggers[i]); + nk_tree_pop(context); + } + if(nk_tree_push(context, NK_TREE_TAB, "Entities", NK_MAXIMIZED)) { for(int i = 0; i < MAX_SCENE_ENTITIES; i++) @@ -2260,7 +2275,6 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* nk_tree_pop(context); } } - /* Enemy */ if(entity->type == ET_ENEMY) @@ -2294,13 +2308,39 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* nk_label(context, "Scan", LABEL_FLAGS_ALIGN_LEFT); nk_labelf(context, LABEL_FLAGS_ALIGN_LEFT, "%s", enemy->Turret.scan ? "True" : "False"); nk_label(context, "Default Color", LABEL_FLAGS_ALIGN_LEFT); editor_widget_color_combov4(context, &enemy->Turret.color_default, 50, row_height * 2); nk_label(context, "Alert Color", LABEL_FLAGS_ALIGN_LEFT); editor_widget_color_combov4(context, &enemy->Turret.color_alert, 50, row_height * 2); - nk_label(context, "Attack Color", LABEL_FLAGS_ALIGN_LEFT); editor_widget_color_combov4(context, &enemy->Turret.color_attack, 50, row_height * 2); + nk_label(context, "Attack Color", LABEL_FLAGS_ALIGN_LEFT); editor_widget_color_combov4(context, &enemy->Turret.color_attack, 50, row_height * 2); } break; } nk_tree_pop(context); } } + + /* Trigger */ + if(entity->type == ET_TRIGGER) + { + struct Trigger* trigger = (struct Trigger*)entity; + if(nk_tree_push(context, NK_TREE_TAB, "Trigger", NK_MAXIMIZED)) + { + nk_layout_row_dynamic(context, row_height, 2); + + float combo_width = nk_widget_width(context), combo_height = row_height * TRIG_MAX; + nk_label(context, "Type", LABEL_FLAGS_ALIGN_LEFT); + int trigger_type = nk_combo_string(context, "Toggle\0Continuous\0One Shot", trigger->type, TRIG_MAX, row_height, nk_vec2(combo_width, combo_height)); + trigger->type = trigger_type; + + nk_label(context, "Triggered", LABEL_FLAGS_ALIGN_LEFT); + nk_label(context, trigger->triggered ? "True" : "False", LABEL_FLAGS_ALIGN_LEFT); + + nk_label(context, "Trigger Count", LABEL_FLAGS_ALIGN_LEFT); + nk_labelf(context, LABEL_FLAGS_ALIGN_LEFT, "%d", trigger->count); + + nk_label(context, "Mask", LABEL_FLAGS_ALIGN_LEFT); + nk_labelf(context, LABEL_FLAGS_ALIGN_LEFT, "%d", trigger->trigger_mask); + + nk_tree_pop(context); + } + } } else { diff --git a/src/game/entity.c b/src/game/entity.c index 09e1a86..3404267 100755 --- a/src/game/entity.c +++ b/src/game/entity.c @@ -271,6 +271,14 @@ bool entity_write(struct Entity* entity, struct Parser_Object* object, bool writ enemy_write(enemy, entity_data); } break; + case ET_TRIGGER: + { + struct Trigger* trigger = (struct Trigger*)entity; + hashmap_int_set(entity_data, "trigger_type", trigger->type); + hashmap_int_set(entity_data, "trigger_mask", trigger->trigger_mask); + hashmap_int_set(entity_data, "trigger_event", trigger->trigger_event); + } + break; }; return true; @@ -512,6 +520,18 @@ struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_e return new_entity; } break; + case ET_TRIGGER: + { + int type = hashmap_value_exists(object->data, "trigger_type") ? hashmap_int_get(object->data, "trigger_type") : TRIG_TOGGLE; + int mask = hashmap_value_exists(object->data, "trigger_mask") ? hashmap_int_get(object->data, "trigger_mask") : TRIGM_ALL; + int trigger_event = hashmap_value_exists(object->data, "trigger_event") ? hashmap_int_get(object->data, "trigger_event") : -1; + struct Trigger* trigger = scene_trigger_create(scene, name, parent_entity, type, trigger_event, mask); + if(!trigger) + return new_entity; + else + new_entity = &trigger->base; + } + break; default: log_warning("Unhandled Entity type '%d' detected", type); break; @@ -628,6 +648,8 @@ const char* entity_type_name_get(struct Entity* entity) case ET_ROOT: typename = "Root"; break; case ET_SOUND_SOURCE: typename = "Sound Source"; break; case ET_STATIC_MESH: typename = "Static Mesh"; break; + case ET_ENEMY: typename = "Enemy"; break; + case ET_TRIGGER: typename = "Trigger"; break; default: typename = "Unknown"; break; }; return typename; diff --git a/src/game/entity.h b/src/game/entity.h index 6cf8633..2a16e48 100755 --- a/src/game/entity.h +++ b/src/game/entity.h @@ -15,6 +15,7 @@ struct Material_Param; struct Parser_Object; typedef void (*Collision_CB)(struct Entity* this_entity, struct Entity* other_entity, Rigidbody, Rigidbody); +typedef void (*Trigger_Func)(struct Trigger* trigger); enum Entity_Type { @@ -27,6 +28,7 @@ enum Entity_Type ET_STATIC_MESH, ET_SOUND_SOURCE, ET_ENEMY, + ET_TRIGGER, ET_MAX }; @@ -74,7 +76,23 @@ enum Entity_Ray_Mask ERM_STATIC_MESH = 1 << 4, ERM_SOUND_SOURCE = 1 << 5, ERM_ENEMY = 1 << 6, - ERM_ALL = ERM_DEFAULT | ERM_PLAYER | ERM_CAMERA | ERM_LIGHT | ERM_STATIC_MESH | ERM_SOUND_SOURCE | ERM_ENEMY + ERM_TRIGGER = 1 << 7, + ERM_ALL = ERM_DEFAULT | ERM_PLAYER | ERM_CAMERA | ERM_LIGHT | ERM_STATIC_MESH | ERM_SOUND_SOURCE | ERM_ENEMY | ERM_TRIGGER +}; + +enum Trigger_Mask +{ + TRIGM_PLAYER = 0, + TRIGM_ENEMY = 1 << 0, + TRIGM_ALL = TRIGM_PLAYER | TRIGM_ENEMY +}; + +enum Trigger_Type +{ + TRIG_TOGGLE = 0, // Toggled on once and fires event then wont fire event until it is deactivated and activated again + TRIG_CONTINUOUS, // Continuously fire events while the trigger is active + TRIG_ONE_SHOT, // Fire event once when triggerd and then get deleted + TRIG_MAX }; struct Transform @@ -226,6 +244,16 @@ struct Enemy }; }; +struct Trigger +{ + struct Entity base; + bool triggered; + int type; + int count; + int trigger_mask; + int trigger_event; // Event to fire when triggered +}; + 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/scene.c b/src/game/scene.c index 179dbb4..c1b22e6 100755 --- a/src/game/scene.c +++ b/src/game/scene.c @@ -21,6 +21,7 @@ #include "enemy.h" #include "event.h" #include "scene_funcs.h" +#include "trigger.h" #include #include @@ -82,6 +83,12 @@ void scene_init(struct Scene* scene) scene->enemies->base.type = ET_ENEMY; } + for(int i = 0; i < MAX_SCENE_TRIGGERS; i++) + { + entity_reset(&scene->triggers[i], i); + scene->triggers->base.type = ET_TRIGGER; + } + player_init(&scene->player, scene); editor_camera_init(game_state->editor, game_state->cvars); editor_init_entities(game_state->editor); @@ -269,6 +276,7 @@ bool scene_save(struct Scene* scene, const char* filename, int directory_type) scene_write_entity_list(scene, ET_CAMERA, parser); scene_write_entity_list(scene, ET_SOUND_SOURCE, parser); scene_write_entity_list(scene, ET_ENEMY, parser); + scene_write_entity_list(scene, ET_TRIGGER, parser); if(parser_write_objects(parser, scene_file, prefixed_filename)) log_message("Scene saved to %s", prefixed_filename); @@ -316,6 +324,11 @@ void scene_write_entity_list(struct Scene* scene, int entity_type, struct Parser entity = &scene->enemies[0].base; stride = sizeof(struct Enemy); break; + case ET_TRIGGER: + max_length = MAX_SCENE_TRIGGERS; + entity = &scene->triggers[0].base; + stride = sizeof(struct Trigger); + break; default: return; } @@ -393,6 +406,12 @@ void scene_update_physics(struct Scene* scene, float fixed_dt) if(scene->enemies[i].base.flags & EF_ACTIVE) enemy_update_physics(&scene->enemies[i], scene, fixed_dt); } + + for(int i = 0; i < MAX_SCENE_TRIGGERS; i++) + { + if(scene->triggers[i].base.flags & EF_ACTIVE) + trigger_update_physics(&scene->triggers[i], scene, fixed_dt); + } } } @@ -505,6 +524,18 @@ void scene_post_update(struct Scene* scene) } + for(int i = 0; i < MAX_SCENE_TRIGGERS; i++) + { + struct Trigger* trigger = &scene->triggers[i]; + if(!(trigger->base.flags & EF_ACTIVE)) continue; + + if(trigger->base.flags & EF_MARKED_FOR_DELETION) + { + scene_trigger_remove(scene, trigger); + continue; + } + } + if(scene->player.base.transform.is_modified) { scene->player.base.transform.is_modified = false; @@ -712,6 +743,32 @@ struct Enemy* scene_enemy_create(struct Scene* scene, const char* name, struct E return new_enemy; } +struct Trigger* scene_trigger_create(struct Scene* scene, const char* name, struct Entity* parent, int type, int trigger_event, int mask) +{ + assert(scene); + struct Trigger* new_trigger = NULL; + for(int i = 0; i < MAX_SCENE_TRIGGERS; i++) + { + struct Trigger* trigger = &scene->triggers[i]; + if(!(trigger->base.flags & EF_ACTIVE)) + { + new_trigger = trigger; + break; + } + } + + if(new_trigger) + { + entity_init(&new_trigger->base, name, parent ? parent : &scene->root_entity); + trigger_init(new_trigger, type, trigger_event, mask); + } + else + { + log_error("scene:trigger_create", "Max trigger limit reached!"); + } + + return new_trigger; +} void scene_entity_base_remove(struct Scene* scene, struct Entity* entity) { assert(scene && entity && entity->id >= 0); @@ -737,6 +794,13 @@ void scene_enemy_remove(struct Scene* scene, struct Enemy* enemy) scene_entity_base_remove(scene, enemy); } +void scene_trigger_remove(struct Scene* scene, struct Trigger* trigger) +{ + assert(scene && trigger); + trigger_reset(trigger); + scene_entity_base_remove(scene, &trigger->base); +} + void scene_camera_remove(struct Scene* scene, struct Camera* camera) { assert(scene && camera); @@ -867,6 +931,23 @@ struct Enemy* scene_enemy_find(struct Scene* scene, const char* name) return enemy; } +struct Trigger* scene_trigger_find(struct Scene* scene, const char* name) +{ + assert(scene && name); + struct Trigger* trigger = NULL; + + for(int i = 0; i < MAX_SCENE_TRIGGERS; i++) + { + if(strncmp(name, scene->triggers[i].base.name, MAX_ENTITY_NAME_LEN) == 0) + { + trigger = &scene->triggers[i]; + break; + } + } + + return trigger; +} + struct Entity* scene_base_entity_get(struct Scene* scene, int id, int type) { assert(scene && id != -1 && type < ET_MAX); @@ -881,6 +962,7 @@ struct Entity* scene_base_entity_get(struct Scene* scene, int id, int type) case ET_STATIC_MESH: entity = &scene->static_meshes[id]; break; case ET_SOUND_SOURCE: entity = &scene->sound_sources[id]; break; case ET_ENEMY: entity = &scene->enemies[id]; break; + case ET_TRIGGER: entity = &scene->triggers[id]; break; case ET_PLAYER: entity = &scene->player; break; case ET_ROOT: entity = &scene->root_entity; break; } diff --git a/src/game/scene.h b/src/game/scene.h index 0d94f4d..62b970b 100755 --- a/src/game/scene.h +++ b/src/game/scene.h @@ -22,6 +22,7 @@ struct Scene struct Light lights[MAX_SCENE_LIGHTS]; struct Sound_Source sound_sources[MAX_SCENE_SOUND_SOURCES]; struct Enemy enemies[MAX_SCENE_ENEMIES]; + struct Trigger triggers[MAX_SCENE_TRIGGERS]; char entity_archetypes[MAX_SCENE_ENTITY_ARCHETYPES][MAX_FILENAME_LEN]; int active_camera_index; Scene_Init_Func init; @@ -43,6 +44,7 @@ struct Camera* scene_camera_create(struct Scene* scene, const char* name, 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); +struct Trigger* scene_trigger_create(struct Scene* scene, const char* name, struct Entity* parent, int type, int trigger_event, int mask); void scene_entity_base_remove(struct Scene* scene, struct Entity* entity); void scene_light_remove(struct Scene* scene, struct Light* light); @@ -50,6 +52,7 @@ 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_trigger_remove(struct Scene* scene, struct Trigger* trigger); 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); @@ -59,6 +62,7 @@ struct Static_Mesh* scene_static_mesh_find(struct Scene* scene, const char* nam 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_find(struct Scene* scene, const char* name); +struct Trigger* scene_trigger_find(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/src/game/trigger.c b/src/game/trigger.c new file mode 100644 index 0000000..0d75664 --- /dev/null +++ b/src/game/trigger.c @@ -0,0 +1,102 @@ +#include "trigger.h" +#include "entity.h" +#include "game.h" +#include "event.h" +#include "bounding_volumes.h" +#include "scene.h" + +#include + +void trigger_init(struct Trigger* trigger, int type, int trigger_event, int trigger_mask) +{ + assert(type < TRIG_MAX); + + trigger->base.type = ET_TRIGGER; + trigger->count = 0; + trigger->triggered = false; + trigger->type = type; + trigger->trigger_event = trigger_event; + trigger->trigger_mask = trigger_mask; +} + +void trigger_reset(struct Trigger* trigger) +{ + +} + +void trigger_update_physics(struct Trigger* trigger, struct Scene* scene, float fixed_dt) +{ + // Check if we're triggered and fire the associated event + bool intersecting = false; + if(trigger->trigger_mask & TRIGM_PLAYER) + { + int intersection = bv_intersect_bounding_boxes(&trigger->base.derived_bounding_box, &scene->player.base.derived_bounding_box); + if(intersection == IT_INSIDE || intersection == IT_INTERSECT) + { + intersecting = true; + } + } + + if(trigger->trigger_mask & TRIGM_ENEMY) + { + for(int i = 0; i < MAX_SCENE_ENEMIES; i++) + { + struct Enemy* enemy = &scene->enemies[i]; + if(!(enemy->base.flags & EF_ACTIVE)) + continue; + + int intersection = bv_intersect_bounding_boxes(&trigger->base.derived_bounding_box, &enemy->mesh->base.derived_bounding_box); + if(intersection == IT_INTERSECT || intersection == IT_INSIDE) + { + intersecting = true; + } + } + } + + if(intersecting) + { + bool fire_event = false; + switch(trigger->type) + { + case TRIG_ONE_SHOT: + { + fire_event = true; + trigger->triggered = true; + scene_trigger_remove(scene, trigger); + } + break; + case TRIG_TOGGLE: + { + // if trigger is already triggered last frame and is also intersecting this frame, then + // it means event has already been fired and we cannot fire an event until it is empty + if(trigger->triggered) + { + fire_event = false; + } + else + { + trigger->triggered = true; + fire_event = true; + } + } + break; + case TRIG_CONTINUOUS: + { + trigger->triggered = true; + fire_event = true; + } + } + + if(fire_event && trigger->trigger_event != -1) + { + struct Event_Manager* event_manager = game_state_get()->event_manager; + struct Event* trigger_event = event_manager_create_new_event(event_manager); + trigger_event->type = trigger->trigger_event; + event_manager_send_event(event_manager, trigger_event); + } + } + else + { + trigger->triggered = false; + } +} diff --git a/src/game/trigger.h b/src/game/trigger.h new file mode 100644 index 0000000..79f2ff4 --- /dev/null +++ b/src/game/trigger.h @@ -0,0 +1,10 @@ +#ifndef TRIGGER_H +#define TRIGGER_H + +struct Trigger; + +void trigger_init(struct Trigger* trigger, int type, int trigger_event, int trigger_mask); +void trigger_reset(struct Trigger* trigger); +void trigger_update_physics(struct Trigger* trigger, struct Scene* scene, float fixed_dt); + +#endif \ No newline at end of file diff --git a/todo.txt b/todo.txt index 76e135e..21e6b95 100644 --- a/todo.txt +++ b/todo.txt @@ -1,10 +1,10 @@ Todo: - - Figure out how to write Scene/Level specific logic, save/load it and be able to differenciate it according to specific scene - - How to specify and track level objectives and track them - - How to move from one scene to another - - Level-wide events and responses - - Main menu as a level or just have a pause menu where game's options etc can be changed? + - Implement Triggers + - Implement flag for ignoring collisions with certain entities - Implement separate property window for player related variables that can be shown in the editor similar to renderer settings etc + - Implement game gui either with a separate nuklear context or as part of existing context + - Property inspector in editor should only show/apply local transformation values and world transformation values should be shown as non-editabale values + - Add "Select Parent" button to property inspector - Fix rotate gizmo's origin not being set to the selected entity - Player shooting - Player jump cooldown, don't allow jump until a certian time interval has passed, even if we're grounded