diff --git a/assets/scenes/Level_1.symtres b/assets/scenes/Level_1.symtres index ed3e831..d920e6a 100755 --- a/assets/scenes/Level_1.symtres +++ b/assets/scenes/Level_1.symtres @@ -16,12 +16,12 @@ Player { type : 2 scale : 1.000 1.000 1.000 - rotation : 0.000 0.102 0.000 -0.997 + rotation : 0.000 0.971 0.000 -0.249 active : true - position : -8.511 2.857 27.549 - bouding_box_min : -0.500 -0.500 -0.500 + position : -30.395 2.857 -17.708 + bouding_box_min : -1.500 -1.500 -1.000 name : Player - bouding_box_max : 0.500 0.500 0.500 + bouding_box_max : 1.500 1.500 1.000 camera_clear_color : 0.310 0.412 0.529 1.000 } @@ -127,7 +127,7 @@ Scene_Entity_Entry Scene_Entity_Entry { scale : 1.000 1.000 1.000 - rotation : 0.000 -0.158 0.000 0.988 + rotation : 0.000 0.001 0.000 1.001 position : 0.000 3.000 0.000 filename : turret name : Turret @@ -136,7 +136,7 @@ Scene_Entity_Entry Scene_Entity_Entry { scale : 1.000 1.000 1.000 - rotation : 0.000 0.001 0.000 1.000 + rotation : 0.000 -0.348 0.000 0.938 position : -13.000 3.000 4.000 filename : turret name : Turret @@ -145,8 +145,8 @@ Scene_Entity_Entry Scene_Entity_Entry { scale : 1.000 1.000 1.000 - rotation : 0.000 0.005 0.000 1.000 - position : 12.000 3.000 6.000 + rotation : 0.000 -0.002 0.000 1.000 + position : -33.000 3.000 8.000 filename : turret name : Turret } @@ -154,7 +154,7 @@ Scene_Entity_Entry Scene_Entity_Entry { scale : 1.000 1.000 1.000 - rotation : 0.000 -0.450 0.000 0.893 + rotation : 0.000 -0.288 0.000 0.958 position : -7.000 3.000 2.000 filename : turret name : Turret @@ -163,7 +163,7 @@ Scene_Entity_Entry Scene_Entity_Entry { scale : 1.000 1.000 1.000 - rotation : 0.000 0.423 0.000 0.906 + rotation : 0.000 0.000 0.000 1.000 position : 5.000 3.000 6.000 filename : turret name : Turret diff --git a/src/common/linmath.c b/src/common/linmath.c index bdbbcff..1db83ed 100755 --- a/src/common/linmath.c +++ b/src/common/linmath.c @@ -204,6 +204,12 @@ float vec3_dot(vec3* v1, vec3* v2) v1->z * v2->z); } +float vec3_angle(vec3* dir1, vec3* dir2) +{ + float dot = vec3_dot(dir1, dir2); + return TO_DEGREES(acosf(dot)); +} + void vec4_fill(vec4* res, float x, float y, float z, float w) { diff --git a/src/common/linmath.h b/src/common/linmath.h index b4b7c23..589d803 100755 --- a/src/common/linmath.h +++ b/src/common/linmath.h @@ -91,6 +91,7 @@ int vec3_equals(vec3* v1, vec3* v2); float vec3_len(vec3* val); float vec3_distance(vec3 p1, vec3 p2); float vec3_dot(vec3* v1, vec3* v2); +float vec3_angle(vec3* dir1, vec3* dir2); /* vec4 */ int vec4_equals(vec4* v1, vec4* v2); diff --git a/src/common/version.h b/src/common/version.h index ab84a1d..9d82d9a 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 321 +#define SYMMETRY_VERSION_REVISION 322 #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 5e2e0e8..0156e9b 100755 --- a/src/game/editor.c +++ b/src/game/editor.c @@ -2226,7 +2226,7 @@ void editor_window_property_inspector(struct nk_context* context, struct Editor* { case ENEMY_TURRET: { - nk_property_float(context, "Turn Speed", 0.f, &enemy->Turret.turn_speed, FLT_MAX, 0.5f, 0.1f); + nk_property_float(context, "Turn Speed", 0.f, &enemy->Turret.turn_speed_default, FLT_MAX, 0.5f, 0.1f); } break; } diff --git a/src/game/enemy.c b/src/game/enemy.c index a8cb9e1..bf335f8 100644 --- a/src/game/enemy.c +++ b/src/game/enemy.c @@ -34,22 +34,25 @@ void enemy_init(struct Enemy* enemy, int type) { case ENEMY_TURRET: { - enemy->health = 100; - enemy->damage = 10; - enemy->Turret.turn_direction_positive = true; - enemy->Turret.scan = false; - enemy->Turret.turn_speed = 10.f; - enemy->Turret.max_turn_angle = 60.f; - enemy->Turret.pulsate_height = 1.5f; - enemy->Turret.pulsate_speed_scale = 0.1f; - enemy->Turret.attack_cooldown = 0.05f; - enemy->Turret.alert_cooldown = 1.f; - enemy->Turret.color_default = (vec4){ 0.f, 1.f, 1.f, 1.f }; - enemy->Turret.color_alert = (vec4){ 1.f, 1.f, 0.f, 1.f }; - enemy->Turret.color_attack = (vec4){ 1.f, 0.f, 0.f, 1.f }; - enemy->Turret.time_elapsed_since_alert = 0.f; - enemy->Turret.time_elapsed_since_attack = 0.f; - enemy->Turret.vision_range = 15.f; + enemy->health = 100; + enemy->damage = 10; + enemy->Turret.yaw_direction_positive = true; + enemy->Turret.scan = false; + enemy->Turret.turn_speed_default = 50.f; + enemy->Turret.turn_speed_when_targetting = 100.f; + enemy->Turret.turn_speed_current = enemy->Turret.turn_speed_default; + enemy->Turret.max_yaw = 60.f; + enemy->Turret.target_yaw = 0.f; + enemy->Turret.pulsate_height = 1.5f; + enemy->Turret.pulsate_speed_scale = 0.1f; + enemy->Turret.attack_cooldown = 0.05f; + enemy->Turret.alert_cooldown = 1.f; + enemy->Turret.color_default = (vec4){ 0.f, 1.f, 1.f, 1.f }; + enemy->Turret.color_alert = (vec4){ 1.f, 1.f, 0.f, 1.f }; + enemy->Turret.color_attack = (vec4){ 1.f, 0.f, 0.f, 1.f }; + enemy->Turret.time_elapsed_since_alert = 0.f; + enemy->Turret.time_elapsed_since_attack = 0.f; + enemy->Turret.vision_range = 15.f; } break; default: @@ -83,7 +86,7 @@ void enemy_static_mesh_set(struct Enemy* enemy, const char* geometry_filename, i void enemy_update(struct Enemy* enemy, struct Scene* scene, float dt) { - static float enemy_update_interval = 1.f / 120.f; + static float enemy_update_interval = 1.f / 60.f; static float time_elapsed_since_last_update = 0.f; time_elapsed_since_last_update += dt; @@ -140,9 +143,9 @@ struct Enemy* enemy_read(struct Parser_Object* object, const char* name, struct { case ENEMY_TURRET: { - if(hashmap_value_exists(object->data, "turn_speed")) new_enemy->Turret.turn_speed = hashmap_float_get(object->data, "turn_speed"); - if(hashmap_value_exists(object->data, "max_turn_angle")) new_enemy->Turret.max_turn_angle = hashmap_float_get(object->data, "max_turn_angle"); - if(hashmap_value_exists(object->data, "turn_direction_positive")) new_enemy->Turret.turn_direction_positive = hashmap_bool_get(object->data, "turn_direction_positive"); + if(hashmap_value_exists(object->data, "turn_speed")) new_enemy->Turret.turn_speed_default = hashmap_float_get(object->data, "turn_speed"); + if(hashmap_value_exists(object->data, "max_turn_angle")) new_enemy->Turret.max_yaw = hashmap_float_get(object->data, "max_turn_angle"); + if(hashmap_value_exists(object->data, "turn_direction_positive")) new_enemy->Turret.yaw_direction_positive = hashmap_bool_get(object->data, "turn_direction_positive"); } break; } @@ -160,9 +163,9 @@ void enemy_write(struct Enemy* enemy, struct Hashmap* entity_data) { case ENEMY_TURRET: { - hashmap_float_set(entity_data, "turn_speed", enemy->Turret.turn_speed); - hashmap_float_set(entity_data, "max_turn_angle", enemy->Turret.max_turn_angle); - hashmap_bool_set(entity_data, "turn_direction_positive", enemy->Turret.turn_direction_positive); + hashmap_float_set(entity_data, "turn_speed", enemy->Turret.turn_speed_default); + hashmap_float_set(entity_data, "max_turn_angle", enemy->Turret.max_yaw); + hashmap_bool_set(entity_data, "turn_direction_positive", enemy->Turret.yaw_direction_positive); } break; } @@ -208,20 +211,20 @@ void enemy_update_physics_turret(struct Enemy* enemy, struct Game_State* game_st { float current_yaw = quat_get_yaw(&enemy->base.transform.rotation); - float yaw = enemy->Turret.turn_speed * 1.f * fixed_dt; - if(!enemy->Turret.turn_direction_positive) + float yaw = enemy->Turret.turn_speed_current * 1.f * fixed_dt; + if(!enemy->Turret.yaw_direction_positive) yaw *= -1.f; current_yaw += yaw; - if(current_yaw >= enemy->Turret.max_turn_angle) + if(current_yaw >= enemy->Turret.max_yaw) { yaw = 0.f; - enemy->Turret.turn_direction_positive = false; + enemy->Turret.yaw_direction_positive = false; } - else if(current_yaw <= -enemy->Turret.max_turn_angle) + else if(current_yaw <= -enemy->Turret.max_yaw) { yaw = 0.f; - enemy->Turret.turn_direction_positive = true; + enemy->Turret.yaw_direction_positive = true; } if(yaw != 0.f) @@ -229,11 +232,15 @@ void enemy_update_physics_turret(struct Enemy* enemy, struct Game_State* game_st } else if(!enemy->Turret.scan) { + float epsilon = 0.5f; float current_yaw = quat_get_yaw(&enemy->base.transform.rotation); - if(fabsf(current_yaw) + EPSILON != 0.f) + float difference = enemy->Turret.target_yaw - current_yaw; + //if(fabsf(current_yaw) > enemy->Turret.target_yaw - EPSILON && fabsf(current_yaw) < enemy->Turret.target_yaw + EPSILON) + if(fabsf(difference) > epsilon) { - float yaw = enemy->Turret.turn_speed * 1.f * fixed_dt; - if(current_yaw > 0.f) + //log_message("Difference %.5f", difference); + float yaw = enemy->Turret.turn_speed_current * 1.f * fixed_dt; + if(current_yaw > enemy->Turret.target_yaw) yaw *= -1.f; transform_rotate(enemy, &yaw_axis, yaw, TS_LOCAL); @@ -297,6 +304,59 @@ void enemy_update_ai_turret(struct Enemy* enemy, struct Game_State* game_state, } } break; + case TURRET_ACQUIRE_TARGET: + { + struct Entity* player = scene_ray_intersect_closest(scene, &turret_ray, ERM_PLAYER); + if(player) + { + float distance = scene_entity_distance(scene, &scene->player, enemy); + if(distance <= enemy->Turret.vision_range) + { + enemy_state_set_turret(enemy, TURRET_ATTACK); + break; + } + } + + float distance = scene_entity_distance(scene, &scene->player, enemy); + if(distance <= enemy->Turret.vision_range) + { + vec3 player_pos = { 0.f }; + transform_get_absolute_position(&scene->player, &player_pos); + vec3 dir_to_player = { 0.f }; + turret_ray.origin.y = 0.f; + player_pos.y = 0.f; + vec3_sub(&dir_to_player, &player_pos, &turret_ray.origin); + vec3_norm(&dir_to_player, &dir_to_player); + im_ray_origin_dir(turret_ray.origin, dir_to_player, 10.f, (vec4) { 0.f, 1.f, 0.f, 1.f }, 5); + //dir_to_player.y = 0.f; + //turret_ray.direction.y = 0.f; + float yaw_required_to_face_player = vec3_angle(&dir_to_player, &turret_ray.direction); + float current_yaw = quat_get_yaw(&enemy->base.transform.rotation); + debug_vars_show_float("Yaw Required", yaw_required_to_face_player); + float new_target_yaw = yaw_required_to_face_player + current_yaw; + if(fabsf(new_target_yaw) > enemy->Turret.max_yaw) + { + log_message("Can't face player"); + log_message("New Yaw : %.3f", new_target_yaw); + log_message("Max yaw : %.3f", enemy->Turret.max_yaw); + log_message("Cur yaw : %.3f", current_yaw); + log_message("Ang bet : %.3f", yaw_required_to_face_player); + enemy_state_set_turret(enemy, TURRET_ALERT); + } + else + { + float difference = fabsf(enemy->Turret.target_yaw - new_target_yaw); + if(difference > 1.f) + enemy->Turret.target_yaw = new_target_yaw; + } + } + else + { + log_message("No target in range"); + enemy_state_set_turret(enemy, TURRET_ALERT); + } + } + break; case TURRET_ATTACK: { enemy->Turret.time_elapsed_since_attack += dt; @@ -304,7 +364,6 @@ void enemy_update_ai_turret(struct Enemy* enemy, struct Game_State* game_state, { im_ray(&turret_ray, enemy->Turret.vision_range, enemy->Turret.color_attack, 4); struct Entity* player = scene_ray_intersect_closest(scene, &turret_ray, ERM_PLAYER); - bool target_lost = false; if(player) { float distance = scene_entity_distance(scene, player, enemy); @@ -316,18 +375,13 @@ void enemy_update_ai_turret(struct Enemy* enemy, struct Game_State* game_state, } else { - target_lost = true; + enemy_state_set_turret(enemy, TURRET_ACQUIRE_TARGET); + log_message("Target too far"); } } else { - target_lost = true; - } - - if(target_lost) - { - enemy_state_set_turret(enemy, TURRET_ALERT); - log_message("Target lost"); + enemy_state_set_turret(enemy, TURRET_ACQUIRE_TARGET); } } } @@ -350,6 +404,8 @@ void enemy_state_set_turret(struct Enemy* enemy, int state) enemy->Turret.time_elapsed_since_attack = 0.f; enemy->Turret.pulsate = true; enemy->Turret.scan = false; + enemy->Turret.target_yaw = 0.f; + enemy->Turret.turn_speed_current = enemy->Turret.turn_speed_default; vec3 default_position = { 0.f }; transform_set_position(enemy->mesh, &default_position); } @@ -357,17 +413,26 @@ void enemy_state_set_turret(struct Enemy* enemy, int state) case TURRET_ALERT: { enemy->Turret.pulsate = false; + enemy->Turret.turn_speed_current = enemy->Turret.turn_speed_default; enemy->Turret.scan = true; + enemy->Turret.target_yaw = enemy->Turret.yaw_direction_positive ? enemy->Turret.max_yaw : -enemy->Turret.max_yaw; vec4_assign(&model->material_params[MMP_DIFFUSE_COL].val_vec4, &enemy->Turret.color_alert); enemy->Turret.time_elapsed_since_alert = 0.f; } break; + case TURRET_ACQUIRE_TARGET: + { + vec4_assign(&model->material_params[MMP_DIFFUSE_COL].val_vec4, &enemy->Turret.color_attack); + enemy->Turret.scan = false; + enemy->Turret.turn_speed_current = enemy->Turret.turn_speed_when_targetting; + } + break; case TURRET_ATTACK: { enemy->Turret.pulsate = false; enemy->Turret.scan = false; vec4_assign(&model->material_params[MMP_DIFFUSE_COL].val_vec4, &enemy->Turret.color_attack); - enemy->Turret.time_elapsed_since_attack = 0.f; + enemy->Turret.time_elapsed_since_attack = enemy->Turret.attack_cooldown; } break; } diff --git a/src/game/enemy.h b/src/game/enemy.h index faaa3ee..6d99de0 100644 --- a/src/game/enemy.h +++ b/src/game/enemy.h @@ -11,6 +11,7 @@ enum Turret_State { TURRET_DEFAULT = 0, TURRET_ALERT, + TURRET_ACQUIRE_TARGET, TURRET_ATTACK, TURRET_STATE_MAX }; diff --git a/src/game/entity.h b/src/game/entity.h index d18ccd2..eaed25d 100755 --- a/src/game/entity.h +++ b/src/game/entity.h @@ -204,9 +204,12 @@ struct Enemy { struct { - float turn_speed; - float max_turn_angle; - bool turn_direction_positive; + float turn_speed_default; + float turn_speed_when_targetting; + float turn_speed_current; + float max_yaw; + float target_yaw; + bool yaw_direction_positive; bool pulsate; bool scan; float pulsate_speed_scale; diff --git a/todo.txt b/todo.txt index 0f920fe..537f9e4 100644 --- a/todo.txt +++ b/todo.txt @@ -1,11 +1,9 @@ Todo: - Implement separate property window for player related variables that can be shown in the editor similar to renderer settings etc - - Implement turret state machine - Add all sound source properties to propery inspector - Add turret properties to property inspector - Add another ambient sound_source entity as child to enemy entity - Fix Turret losing target at diagonals - - Track current height of the turret and return it to default once pulsation resumes - 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 @@ -415,4 +413,5 @@ Done: * Implemented on_load and on_update callbacks for enemies. Different enemy types have different callbacks that are assigned when they are created. * Added Scene reload command * Introduced fixed time step interval which can be configured and be used for movement and other physics related updates - * Made movement framerate independent \ No newline at end of file + * Made movement framerate independent + * Implement turret state machine \ No newline at end of file