Adds V3 AI animation driver logic

Fixes sv navmesh loading
Adds closed door checking to sv navmesh
Adds min / max util math functions
Removes entrance_edge from navmesh polygons
This commit is contained in:
blubs 2023-08-05 22:53:44 -07:00
parent 09563f8670
commit 47188f6632
8 changed files with 413 additions and 588 deletions

View file

@ -246,24 +246,24 @@ void cl_navmesh_draw_poly(float poly_index) {
}
// If polygon's entrance edge is set, draw other edges as red.
if(cl_navmesh_polies[poly_index].entrance_edge != -1) {
for(int i = 0; i < cl_navmesh_polies[poly_index].vert_count; i++) {
if(i == cl_navmesh_polies[poly_index].entrance_edge)
continue;
// if(cl_navmesh_polies[poly_index].entrance_edge != -1) {
// for(int i = 0; i < cl_navmesh_polies[poly_index].vert_count; i++) {
// if(i == cl_navmesh_polies[poly_index].entrance_edge)
// continue;
// Draw this edge as solid red
int edge_vert_a_idx = i;
int edge_vert_b_idx = (i + 1) % cl_navmesh_polies[poly_index].vert_count;
int edge_vert_a = cl_navmesh_polies[poly_index].verts[edge_vert_a_idx];
int edge_vert_b = cl_navmesh_polies[poly_index].verts[edge_vert_b_idx];
vector start = cl_navmesh_verts[edge_vert_a].pos;
vector end = cl_navmesh_verts[edge_vert_b].pos;
// // Draw this edge as solid red
// int edge_vert_a_idx = i;
// int edge_vert_b_idx = (i + 1) % cl_navmesh_polies[poly_index].vert_count;
// int edge_vert_a = cl_navmesh_polies[poly_index].verts[edge_vert_a_idx];
// int edge_vert_b = cl_navmesh_polies[poly_index].verts[edge_vert_b_idx];
// vector start = cl_navmesh_verts[edge_vert_a].pos;
// vector end = cl_navmesh_verts[edge_vert_b].pos;
vector edge_color = [0.9,0.2,0.2];
float edge_alpha = 0.7;
cl_navmesh_draw_line(start,end,2,edge_color,edge_alpha);
}
}
// vector edge_color = [0.9,0.2,0.2];
// float edge_alpha = 0.7;
// cl_navmesh_draw_line(start,end,2,edge_color,edge_alpha);
// }
// }
}
@ -315,9 +315,9 @@ void cl_navmesh_draw_traversal(float traversal_index) {
float edge_width = 0.1;
if(cl_navmesh_selected_traversal != traversal_index) {
active_alpha = 0.2;
active_alpha = 0.4;
inactive_alpha = 0.0;
edge_alpha = 0.1;
edge_alpha = 0.4;
edge_color = '0 0.2 0.5';
active_color = '0 0.2 0.5';
}
@ -1115,7 +1115,7 @@ void cl_navmesh_make_poly()
cl_navmesh_polies[cl_navmesh_poly_count].verts[i] = selected_verts[i];
}
cl_navmesh_polies[cl_navmesh_poly_count].vert_count = selected_vert_count;
cl_navmesh_polies[cl_navmesh_poly_count].entrance_edge = -1;
// cl_navmesh_polies[cl_navmesh_poly_count].entrance_edge = -1;
cl_navmesh_poly_count++;
}
@ -1133,7 +1133,7 @@ void cl_navmesh_delete_poly_at_index(float poly_index)
}
// Copy down other fields:
cl_navmesh_polies[i].entrance_edge = cl_navmesh_polies[i+1].entrance_edge;
// cl_navmesh_polies[i].entrance_edge = cl_navmesh_polies[i+1].entrance_edge;
cl_navmesh_polies[i].doortarget = cl_navmesh_polies[i+1].doortarget;
}
@ -1143,7 +1143,7 @@ void cl_navmesh_delete_poly_at_index(float poly_index)
{
cl_navmesh_polies[cl_navmesh_poly_count-1].verts[j] = -1;
}
cl_navmesh_polies[cl_navmesh_poly_count-1].entrance_edge = -1;
// cl_navmesh_polies[cl_navmesh_poly_count-1].entrance_edge = -1;
cl_navmesh_polies[cl_navmesh_poly_count-1].doortarget = "";
cl_navmesh_poly_count--;
@ -1695,7 +1695,7 @@ void cl_navmesh_editor_save_navmesh() {
// Getting polygon entrance edge index
fputs(file,itos(cl_navmesh_polies[i].entrance_edge),"\n");
// fputs(file,itos(cl_navmesh_polies[i].entrance_edge),"\n");
}
// -----------------------------------------------------------------------
@ -1773,7 +1773,7 @@ void cl_navmesh_editor_clear_navmesh()
cl_navmesh_polies[i].doortarget = "";
cl_navmesh_polies[i].entrance_edge = -1;
// cl_navmesh_polies[i].entrance_edge = -1;
}
cl_navmesh_traversal_count = 0;
for(float i = 0; i < NAV_MAX_TRAVERSALS; i++) {
@ -1805,19 +1805,17 @@ void cl_navmesh_editor_load_navmesh() {
}
float v;
//First line contains navmesh file semver
// First line contains navmesh file semver
string nav_file_version = fgets(file);
print("Loading v", nav_file_version, " navmesh file \"", filepath, "\"...\n");
// TODO - Add backwards compatibility for older navmesh file versions.
// Next line contains vertex count
string line = fgets(file);
float vert_count = stof(line);
float vert_count = stof(fgets(file));
if(vert_count > NAV_MAX_VERTS) {
print("Error: navmesh file \"",filepath,"\" has an invalid vert count. (" , line, " > ", ftos(NAV_MAX_VERTS),").\n");
print("Error: navmesh file \"",filepath,"\" has an invalid vert count. (" , vert_count, " > ", ftos(NAV_MAX_VERTS),").\n");
fclose(file);
return;
}
@ -1826,20 +1824,12 @@ void cl_navmesh_editor_load_navmesh() {
cl_navmesh_editor_clear_navmesh();
cl_navmesh_vert_count = vert_count;
print("Vert count: ",line,"\n");
print("Vert count: ",vert_count,"\n");
//Temp vector to assign component-wise
vector temp;
//Reading all of the vertex positions
for(float i = 0; i < cl_navmesh_vert_count; i++) {
line = fgets(file);
temp = stov(line);
cl_navmesh_verts[i].pos.x = temp.x;
cl_navmesh_verts[i].pos.y = temp.y;
cl_navmesh_verts[i].pos.z = temp.z;
//TODO: if something goes wrong, clear the partially loaded navmesh
cl_navmesh_verts[i].pos = stov(fgets(file));
}
//Next line contains the number of polygons
@ -1881,7 +1871,7 @@ void cl_navmesh_editor_load_navmesh() {
// If navmesh file version 0.0.0, no traversals
// FIXME - Need a better way to do this..
if(strcmp(nav_file_version, "0.0.0") != 0) {
if(nav_file_version != "0.0.0") {
// Don't care about connected traversals
float n_traversals = stof(fgets(file));
for(float j = 0; j < n_traversals; j++) {
@ -1896,8 +1886,11 @@ void cl_navmesh_editor_load_navmesh() {
print("CLLOADNAVMESH - Polygon at index ",ftos(i)," has doortarget: \"", cl_navmesh_polies[i].doortarget, "\"" );
}
// cl_navmesh_polies[i].doortarget = ""; // Temp fix to load old files
// Load entrance edge
cl_navmesh_polies[i].entrance_edge = stoi(fgets(file));
// If v0.0.0, discard entrance edge (no longer used)
if(nav_file_version == "0.0.0") {
// cl_navmesh_polies[i].entrance_edge = stoi(fgets(file));
fgets(file);
}
// cl_navmesh_polies[i].entrance_edge = -1; // Temp fix to load old files
}
@ -1905,27 +1898,15 @@ void cl_navmesh_editor_load_navmesh() {
// Load Traversals
// -----------------------------------------------------------------------
// If navmesh file version 0.0.0, no traversals
if(strcmp(nav_file_version, "0.0.0") != 0) {
if(nav_file_version != "0.0.0") {
// Next line contains the number of traverals
cl_navmesh_traversal_count = stof(fgets(file));
// Next lines are each traversal
for(float i = 0; i < cl_navmesh_traversal_count; i++) {
line = fgets(file);
temp = stov(line);
cl_navmesh_traversals[i].start_pos.x = temp.x;
cl_navmesh_traversals[i].start_pos.y = temp.y;
cl_navmesh_traversals[i].start_pos.z = temp.z;
line = fgets(file);
temp = stov(line);
cl_navmesh_traversals[i].midpoint_pos.x = temp.x;
cl_navmesh_traversals[i].midpoint_pos.y = temp.y;
cl_navmesh_traversals[i].midpoint_pos.z = temp.z;
line = fgets(file);
temp = stov(line);
cl_navmesh_traversals[i].end_pos.x = temp.x;
cl_navmesh_traversals[i].end_pos.y = temp.y;
cl_navmesh_traversals[i].end_pos.z = temp.z;
cl_navmesh_traversals[i].start_pos = stov(fgets(file));
cl_navmesh_traversals[i].midpoint_pos = stov(fgets(file));
cl_navmesh_traversals[i].end_pos = stov(fgets(file));
cl_navmesh_traversals[i].angle = stof(fgets(file));
cl_navmesh_traversals[i].use_midpoint = stof(fgets(file));
// Don't care about traversal end polygon index
@ -2620,6 +2601,9 @@ void cl_pathfind_trace_path(float start_poly, float goal_poly) {
//Returns 1 on success.
//Returns 0 on fail.
float cl_navmesh_pathfind_start(float start_poly, float goal_poly, vector start_pos, vector end_pos) {
//Clearing previous data
cl_pathfind_clear_result_data();
if(start_poly == -1) {
print("Error: pathfind start node invalid.\n");
return 0;
@ -2645,9 +2629,6 @@ float cl_navmesh_pathfind_start(float start_poly, float goal_poly, vector start_
return 1;
}
//Clearing previous data
cl_pathfind_clear_result_data();
//Adding start polygon to the open set
cl_test_pathfind_result->poly_set[start_poly] = PATHFIND_POLY_SET_OPEN;
cl_test_pathfind_result->poly_g_score[start_poly] = 0;
@ -2693,67 +2674,6 @@ float cl_navmesh_pathfind_start(float start_poly, float goal_poly, vector start_
// NOTE - This check isn't done in the navmesh editor.
// ----------------------------------------------------------------
// // ----------------------------------------------------------------
// // Entrance edge
// // ----------------------------------------------------------------
// // Check if we can enter this polygon from this edge
// // If entrance_edge != -1, we can only enter the polygon from the edge at index "entrance_edge"
// if(cl_navmesh_polies[neighbor].entrance_edge != -1) {
// // print("-- Pathfind loop -- Evaluating a neighbor whose entrance_edge = ",ftos(cl_navmesh_polies[neighbor].entrance_edge),"\n");
// // print("Current polygon:",ftos(current),"\n");
// // print("Neighbor polygon:",ftos(neighbor),"\n");
// // print("Neighbor index:",ftos(i),"\n");
// // print("Current vertices: ");
// // for(float j = 0; j < cl_navmesh_polies[current].vert_count; j++) {
// // print(ftos(cl_navmesh_polies[current].verts[j]),",");
// // }
// // print("\n");
// // print("Neighbor vertices: ");
// // for(float j = 0; j < cl_navmesh_polies[neighbor].vert_count; j++) {
// // print(ftos(cl_navmesh_polies[neighbor].verts[j]),",");
// // }
// // print("\n");
// // print("Current left portal vertices: ");
// // for(float j = 0; j < cl_navmesh_polies[current].connected_polies_count; j++) {
// // print(ftos(cl_navmesh_polies[current].connected_polies_left_vert[j]),",");
// // }
// // print("\n");
// // print("Current right portal vertices: ");
// // for(float j = 0; j < cl_navmesh_polies[current].connected_polies_count; j++) {
// // print(ftos(cl_navmesh_polies[current].connected_polies_right_vert[j]),",");
// // }
// // print("\n");
// // print("neighbor left portal vertices: ");
// // for(float j = 0; j < cl_navmesh_polies[neighbor].connected_polies_count; j++) {
// // print(ftos(cl_navmesh_polies[neighbor].connected_polies_left_vert[j]),",");
// // }
// // print("\n");
// // print("neighbor right portal vertices: ");
// // for(float j = 0; j < cl_navmesh_polies[neighbor].connected_polies_count; j++) {
// // print(ftos(cl_navmesh_polies[neighbor].connected_polies_right_vert[j]),",");
// // }
// // print("\n");
// // print("Current neighbor edge index: ");
// // for(float j = 0; j < cl_navmesh_polies[current].connected_polies_count; j++) {
// // print(ftos(cl_navmesh_polies[current].connected_polies_neighbor_edge_index[j]),",");
// // }
// // print("\n");
// // print("Neighbor neighbor edge index: ");
// // for(float j = 0; j < cl_navmesh_polies[neighbor].connected_polies_count; j++) {
// // print(ftos(cl_navmesh_polies[neighbor].connected_polies_neighbor_edge_index[j]),",");
// // }
// // print("\n");
// // print("According to current polygon, the we're traversing the neighbor's edge with index: ", ftos(cl_navmesh_polies[current].connected_polies_neighbor_edge_index[i])," to enter neighbor.\n");
// // Check if the edge we're crossing from current to neighbor is the entrance edge
// if(cl_navmesh_polies[neighbor].entrance_edge != cl_navmesh_polies[current].connected_polies_neighbor_edge_index[i]) {
// // If it's not the entrance edge, skip this neighbor.
// // We can't walk from current to neighbor.
// continue;
// }
// }
// // ----------------------------------------------------------------
if(cl_test_pathfind_result->poly_set[neighbor_poly] != PATHFIND_POLY_SET_CLOSED) {
//print("Neighbor is not in closed list.\n");
@ -3086,7 +3006,7 @@ void cl_register_navmesh_commands() =
registercommand("yes");
registercommand("nav_load_navmesh");
registercommand("nav_toggle_poly_door");
registercommand("nav_toggle_poly_entrance_edge");
// registercommand("nav_toggle_poly_entrance_edge");
registercommand("nav_print_poly_door");
// --- Traversal Commands ---
@ -3121,7 +3041,8 @@ void cl_navmesh_editor_toggle_poly_door() {
string editor_active_door = strcat(cvar_string("nav_editor_active_door"));
// If current polygon doortarget isn't set to the editor's currently active door, set it
if(strcmp(cl_navmesh_polies[selected_polygon].doortarget, editor_active_door) != 0) {
// if(strcmp(cl_navmesh_polies[selected_polygon].doortarget, editor_active_door) != 0) {
if(cl_navmesh_polies[selected_polygon].doortarget != editor_active_door) {
cl_navmesh_polies[selected_polygon].doortarget = editor_active_door;
}
// Otherwise, clear it.
@ -3151,23 +3072,23 @@ void cl_navmesh_editor_print_poly_door() {
}
void cl_navmesh_editor_toggle_entrance_edge() {
int selected_polygon = cl_navmesh_get_selected_poly();
if(selected_polygon == -1) {
print("Can't make door polygon. No polygon selected.\n");
return;
}
// void cl_navmesh_editor_toggle_entrance_edge() {
// int selected_polygon = cl_navmesh_get_selected_poly();
// if(selected_polygon == -1) {
// print("Can't make door polygon. No polygon selected.\n");
// return;
// }
cl_navmesh_polies[selected_polygon].entrance_edge += 1;
// If entrance edge index is no longer valid, reset it to -1
if(cl_navmesh_polies[selected_polygon].entrance_edge >= cl_navmesh_polies[selected_polygon].vert_count) {
cl_navmesh_polies[selected_polygon].entrance_edge = -1;
}
// cl_navmesh_polies[selected_polygon].entrance_edge += 1;
// // If entrance edge index is no longer valid, reset it to -1
// if(cl_navmesh_polies[selected_polygon].entrance_edge >= cl_navmesh_polies[selected_polygon].vert_count) {
// cl_navmesh_polies[selected_polygon].entrance_edge = -1;
// }
print("Selected Polygon entrance edge set to: ");
print(itos(cl_navmesh_polies[selected_polygon].entrance_edge));
print("\n");
}
// print("Selected Polygon entrance edge set to: ");
// print(itos(cl_navmesh_polies[selected_polygon].entrance_edge));
// print("\n");
// }
@ -3579,21 +3500,21 @@ float(string cmd) cl_navmesh_console_commands =
case "nav_print_poly_door":
cl_navmesh_editor_print_poly_door();
return TRUE;
case "nav_toggle_poly_entrance_edge":
cl_navmesh_editor_toggle_entrance_edge();
// TODO - Make currently selected polygon only be traversible from
// TODO one direction to another.
// TODO
// TODO - If no polygon selected, stop.
// TODO - If Quad, assume verts sorted CCW from BL
// TODO - if not one-way, make one-way poly from 01 to 23
// TODO - if one-way from 01 to 23, make one-way from 12 to 03
// TODO - if one-way from 12 to 03, make one-way from 23 to 01
// TODO - if one-way from 12 to 03, make one-way from 03 to 12
// TODO - if one-way from 03 to 12, make normal polygon
// TODO - If tri, should I support one-way? (probably not, tbh)
// TODO
return TRUE;
// case "nav_toggle_poly_entrance_edge":
// cl_navmesh_editor_toggle_entrance_edge();
// // TODO - Make currently selected polygon only be traversible from
// // TODO one direction to another.
// // TODO
// // TODO - If no polygon selected, stop.
// // TODO - If Quad, assume verts sorted CCW from BL
// // TODO - if not one-way, make one-way poly from 01 to 23
// // TODO - if one-way from 01 to 23, make one-way from 12 to 03
// // TODO - if one-way from 12 to 03, make one-way from 23 to 01
// // TODO - if one-way from 12 to 03, make one-way from 03 to 12
// // TODO - if one-way from 03 to 12, make normal polygon
// // TODO - If tri, should I support one-way? (probably not, tbh)
// // TODO
// return TRUE;
case "nav_place_traversal":
cl_navmesh_editor_place_traversal();
return TRUE;

View file

@ -1,19 +1,24 @@
// FIXME - This framegroup nonsense is causing chaos... the struct is too large apparently... what can I cut out?
void init_framegroup( float start_frame, float end_frame, float duration,
void(entity) frame_callback,
void(entity) think_callback,
__out framegroup fg) {
fg.start_frame = start_frame;
fg.end_frame = end_frame;
fg.duration = duration;
fg.frame_callback = frame_callback;
fg.think_callback = think_callback;
}
DEFINE_ANIM(zombie_idle, 1,2,3,4,5,6,7,8,9,10,11,12,13); // Defines: get_anim_frame_zombie_idle, get_anim_length_zombie_idle
DEFINE_ANIM(zombie_rise, 14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37); // Defines: get_anim_frame_zombie_rise, get_anim_length_zombie_rise
DEFINE_ANIM(zombie_walk1, 38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53); // Defines: get_anim_frame_zombie_walk1, get_anim_length_zombie_walk1
DEFINE_ANIM(zombie_walk2, 54,55,56,57,58,59,60,61,62,63,64,65,66,67); // Defines: get_anim_frame_zombie_walk2, get_anim_length_zombie_walk2
DEFINE_ANIM(zombie_walk3, 68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83); // Defines: get_anim_frame_zombie_walk3, get_anim_length_zombie_walk3
DEFINE_ANIM(zombie_jog1, 84,85,86,87,88,89,90,91,92); // Defines: get_anim_frame_zombie_jog1, get_anim_length_zombie_jog1
DEFINE_ANIM(zombie_run1, 93,94,95,96,97,98,99,100,101,102); // Defines: get_anim_frame_zombie_run1, get_anim_length_zombie_run1
DEFINE_ANIM(zombie_attack1, 103,104,105,106,107); // Defines: get_anim_frame_zombie_attack1, get_anim_length_zombie_attack1
DEFINE_ANIM(zombie_attack2, 108,109,110,111,112,113); // Defines: get_anim_frame_zombie_attack2, get_anim_length_zombie_attack2
DEFINE_ANIM(zombie_window_rip_board1, 182,183,184,185,186,187,188,189,190,191,192); // Defines: get_anim_frame_zombie_window_rip_board1, get_anim_length_zombie_window_rip_board1
DEFINE_ANIM(zombie_window_rip_board2, 192,193,194,195,196,197,198,199,200,201,202); // Defines: get_anim_frame_zombie_window_rip_board2, get_anim_length_zombie_window_rip_board2
DEFINE_ANIM(zombie_window_attack, 202,203,204,205,206,207,208,209,210,211); // Defines: get_anim_frame_zombie_window_attack, get_anim_length_zombie_window_attack
DEFINE_ANIM(zombie_window_hop, 114,115,116,117,118,119,120,121,122,123); // Defines: get_anim_frame_zombie_window_hop, get_anim_length_zombie_window_hop
DEFINE_ANIM(zombie_die1, 124,125,126,127,128,129,130,131,132,133,134); // Defines: get_anim_frame_zombie_die1, get_anim_length_zombie_die1
DEFINE_ANIM(zombie_die2, 135,136,137,138,139); // Defines: get_anim_frame_zombie_die2, get_anim_length_zombie_die2
DEFINE_ANIM(zombie_die3, 140,141,142,143,144,145,146,147,148,149); // Defines: get_anim_frame_zombie_die3, get_anim_length_zombie_die3
DEFINE_ANIM(zombie_die_wunder, 212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228); // Defines: get_anim_frame_zombie_die_wunder, get_anim_length_zombie_die_wunder
DEFINE_ANIM(zombie_fall, 150,151,152,153); // Defines: get_anim_frame_zombie_fall, get_anim_length_zombie_fall
DEFINE_ANIM(zombie_land, 154,155,156,157,158,159,160); // Defines: get_anim_frame_zombie_land, get_anim_length_zombie_land
DEFINE_ANIM(zombie_jump, 161,162,163,164,165,166,167); // Defines: get_anim_frame_zombie_jump, get_anim_length_zombie_jump
DEFINE_ANIM(zombie_climb, 168,169,170,171,172,173,174,175,176,177,178,179,180,181,182); // Defines: get_anim_frame_zombie_climb, get_anim_length_zombie_climb
@ -22,140 +27,73 @@ void() AI_Chase::AI_Chase = {
this.path_target = world;
this.path_pos = world.origin;
this.think_delta_time = 0.1; // Call `this.think();` 10x per second
this.cur_fg_idx = 0;
};
void() AI_Chase::think = {
if(this.cur_fg_start_time >= 0) {
float cur_framegroup_idx = this.cur_fg_idx;
float dt = time - this.cur_fg_start_time;
float lerp_frac = (dt / this.cur_fg_duration);
// Frame ranges are inclusive, do an un-clamped interpolation to include the final frame
float cur_frame = unclamped_lerp(this.cur_fg_start_frame, this.cur_fg_end_frame, lerp_frac);
// For rendering, keep frame number always within current animation
if(this.cur_fg_end_frame > this.cur_fg_start_frame) {
this.frame = clamp(cur_frame, this.cur_fg_start_frame, this.cur_fg_end_frame);
}
else {
this.frame = clamp(cur_frame, this.cur_fg_end_frame, this.cur_fg_start_frame);
}
// print("Cur frame: ");
// print(ftos(this.frame));
// print(", start_frame: ");
// print(ftos(this.cur_fg_start_frame));
// print(", end_frame: ");
// print(ftos(this.cur_fg_end_frame));
// print(", cur_frame: ");
// print(ftos(cur_frame));
// print(", next frame callback: ");
// print(ftos(this.cur_fg_frame_callback_next_frame));
// print(", duration: ");
// print(ftos(this.cur_fg_duration));
// print("\n");
// ------------------------------------------------------------------------
// Animation logic
// ------------------------------------------------------------------------
float cur_anim_prev_frame_idx = floor(this.cur_anim_frame_idx);
// TODO - round frame to nearest frame?
// Check if we're updating the current animation being played
if(this.cur_anim_get_frame_func != SUB_Null) {
this.cur_anim_frame_idx = (time - this.cur_anim_start_time) / this.cur_anim_frametime;
// If the animation is playing forward (low frame num to high frame num)
if(this.cur_fg_end_frame > this.cur_fg_start_frame) {
if(cur_frame >= this.cur_fg_frame_callback_next_frame) {
if(cur_frame >= this.cur_fg_end_frame + 1) {
this.cur_fg_frame_callback_is_end_frame = true;
}
this.cur_fg_frame_callback(this); // FIXME - This can override and invoke new things, need to be careful
// Callback may have assigned new framegroup
if(this.cur_fg_idx == cur_framegroup_idx) {
this.cur_fg_frame_callback_is_start_frame = false;
this.cur_fg_frame_callback_is_end_frame = false;
this.cur_fg_frame_callback_next_frame = floor(cur_frame) + 1;
}
// print("\tSet next frame callback frame to: ");
// print(ftos(this.cur_fg_frame_callback_next_frame));
// print("\n");
// If at the final frame, we're done
if(this.cur_anim_frame_idx >= this.cur_anim_length) {
if(this.cur_anim_stop_type == ANIM_STOP_TYPE_LOOP) {
this.cur_anim_frame_idx = this.cur_anim_frame_idx % this.cur_anim_length;
}
else if(this.cur_anim_stop_type == ANIM_STOP_TYPE_NEXT_ANIM) {
this.play_anim(
this.next_anim_get_frame_func,
this.next_anim_length,
this.next_anim_stop_type);
this.cur_anim_frametime = this.next_anim_frametime;
// We started a new animation, play the frame callback
cur_anim_prev_frame_idx = -1;
}
// Otherwise, treat it as ANIM_STOP_TYPE_STOP
else {
this.cur_anim_frame_idx = min(this.cur_anim_frame_idx, this.cur_anim_length - 1);
this.cur_anim_get_frame_func = (float(float)) SUB_Null;
this.cur_anim_length = 0;
}
}
// else the animation is playing in reverse (high frame num to low frame num)
else {
if(cur_frame <= this.cur_fg_frame_callback_next_frame) {
if(cur_frame <= this.cur_fg_end_frame - 1) {
this.cur_fg_frame_callback_is_end_frame = true;
}
this.cur_fg_frame_callback(this);
// Callback may have assigned new framegroup
if(this.cur_fg_idx == cur_fg_idx) {
this.cur_fg_frame_callback_is_start_frame = false;
this.cur_fg_frame_callback_is_end_frame = false;
this.cur_fg_frame_callback_next_frame = ceil(cur_frame) - 1;
}
// If we still have an animation:
if(this.cur_anim_get_frame_func != SUB_Null) {
// NOTE - We can control frame interpolation using the fractional
// NOTE portion of `cur_anim_frame_idx`, in case we ever have
// NOTE better control over MDL frame interpolation.
this.cur_anim_frame_idx = floor(this.cur_anim_frame_idx);
this.frame = this.cur_anim_get_frame_func(this.cur_anim_frame_idx);
if(floor(this.cur_anim_frame_idx) != cur_anim_prev_frame_idx) {
this.frame_callback();
}
}
// Callback may have assigned new framegroup
// if(this.cur_fg_idx == cur_fg_idx) {
this.cur_fg_think_callback(this);
// }
// print("Frame_idx: ", ftos(this.cur_anim_frame_idx), " Actual frame: ", ftos(this.frame), "\n");
}
// ------------------------------------------------------------------------
this.nextthink = time + this.think_delta_time;
};
void (float duration) AI_Chase::set_framegroup_duration = {
// If no current framegroup, stop
if(this.cur_fg_start_time < 0) {
print("Warning: Attempted to set framegroup duration without an active framegroup.\n");
return;
}
this.cur_fg_duration = duration;
// --------------------------------------------------------------------
// Adjust think_delta_time so no frames are skipped
// --------------------------------------------------------------------
float n_anim_frames = fabs(this.cur_fg_end_frame - this.cur_fg_start_frame);
float time_per_frame = duration / n_anim_frames;
// If faster than 10FPS, make thinks be called more often to not miss a frame callback
if(time_per_frame < 0.10) {
this.think_delta_time = time_per_frame * 0.5;
}
else {
this.think_delta_time = 0.1;
}
// --------------------------------------------------------------------
};
void (framegroup fgroup) AI_Chase::set_framegroup = {
// Increment framegroup counter. Reset every 100, we only use this to check when it has changed
this.cur_fg_idx = (this.cur_fg_idx + 1) % 100;
// FIXME - frame callback isn't guaranteed to be called for every frame because sometimes we skip over frames
// FIXME - I should modify the think delta time to run at 80% of the time between frames, or 0.10 seconds, whichever is greater.
// FIXME - That way, if are playing an animation really fast, it'll call think more often
this.cur_fg_start_frame = fgroup.start_frame;
this.cur_fg_end_frame = fgroup.end_frame;
this.cur_fg_frame_callback = fgroup.frame_callback;
this.cur_fg_think_callback = fgroup.think_callback;
this.set_framegroup_duration(fgroup.duration);
// Start the animation now
this.cur_fg_frame_callback_is_start_frame = true;
this.cur_fg_frame_callback_is_end_frame = false;
this.frame = this.cur_fg_start_frame;
this.cur_fg_start_time = time;
this.cur_fg_frame_callback_next_frame = this.cur_fg_start_frame;
// print("Done setting framegroup! Start frame: ");
// print(ftos(fgroup.start_frame));
// print(", End frame: ");
// print(ftos(fgroup.end_frame));
// print("\n");
this.think_callback();
};
// Computes the length of a vector ignoring the Z-component
float (vector ofs) vlen_xy = {
return sqrt(ofs.x * ofs.x + ofs.y * ofs.y);
}
void (float dist) AI_Chase::do_walk_to_goal = {
if(dist == 0) {
void (float move_speed) AI_Chase::do_walk_to_goal = {
if(move_speed == 0) {
return;
}
@ -179,7 +117,7 @@ void (float dist) AI_Chase::do_walk_to_goal = {
// TOOD - If close, continue to next one...
// TODO - Also make sure we're "close enough" in Z...
if(vlen_xy(this.origin - goal_pos) < 20) {
if(vlen_xy(this.origin - goal_pos) < 5) {
print("Distance from target: ", ftos(vlen_xy(this.origin - goal_pos)), ", Cur point: ", ftos(this.pathfind_cur_point_idx), "\n");
print("Current traversal: ", ftos(res->point_path_traversals[this.pathfind_cur_point_idx]),"\n");
// If this point path is a traversal, teleport to traversal end spot
@ -193,23 +131,21 @@ void (float dist) AI_Chase::do_walk_to_goal = {
}
}
else {
dist = 0;
move_speed = 0;
// TODO - Idle animation?
}
#endif // PC
this.ideal_yaw = vectoyaw(goal_pos - this.origin);
ChangeYaw();
vector new_velocity;
float walk_dist = move_speed * this.think_delta_time;
float dist_to_goal = vlen(goal_pos - this.origin);
if(dist > dist_to_goal) {
dist = dist_to_goal;
if(walk_dist > dist_to_goal) {
move_speed = dist_to_goal / this.think_delta_time;
}
new_velocity = normalize(goal_pos - this.origin) * dist;
new_velocity = normalize(goal_pos - this.origin) * move_speed;
new_velocity_z = this.velocity_z;
this.velocity = new_velocity;
};
@ -225,28 +161,11 @@ void (float dist) AI_Chase::do_walk_to_goal = {
framegroup fg_zombie_walk_0;
framegroup fg_zombie_walk_1;
framegroup fg_zombie_walk_2;
framegroup fg_zombie_jog_0;
framegroup fg_zombie_run_0;
// framegroup fg_zombie_run_1; // TODO - Either add new anims, or make new run FG with different speed?
// framegroup fg_zombie_run_2; // TODO - Either add new anims, or make new run FG with different speed?
framegroup fg_zombie_idle_0;
framegroup fg_zombie_attack_0;
framegroup fg_zombie_attack_1;
framegroup fg_zombie_die_0;
framegroup fg_zombie_die_1;
framegroup fg_zombie_die_2;
float get_zombie_walk_is_footstep(float frame) {
frame = floor(frame);
switch(frame) {
switch(floor(frame)) {
// ------------ Zombie Walk 1 --------------
case 39: // Right foot
case 44: // Left foot
@ -259,49 +178,45 @@ float get_zombie_walk_is_footstep(float frame) {
}
float get_zombie_walk_dist_for_frame(float frame) {
frame = floor(frame);
switch(frame) {
switch(floor(frame)) {
// ------------ Zombie Walk 1 --------------
case 37:
return 8;
case 38:
return 8;
case 39:
case 40:
case 41:
case 42:
return 3.5;
case 43:
return 8.8;
return 3.5;
case 44:
return 9;
return 8.8;
case 45:
return 9;
case 46:
return 4;
case 47:
return 7.8;
return 4;
case 48:
return 5.2;
return 7.8;
case 49:
return 2.4;
return 5.2;
case 50:
return 2.8;
return 2.4;
case 51:
return 6.5;
return 2.8;
case 52:
return 6.5;
case 53:
return 7.7;
default:
return 0.0;
}
}
// Called at the first frame of the current animation
void zombie_walk_start_callback(entity ent) {
// print("start called\n");
};
// Called once per animation frame:
void zombie_walk_frame_callback(entity ent) {
AI_Zombie zombie_ent = (AI_Zombie) ent;
void zombie_walk_frame_callback() {
AI_Zombie zombie_ent = (AI_Zombie) self;
// print("frame called\n");
// print("frame called. At frame: ");
// print(ftos(ent.frame));
@ -311,7 +226,7 @@ void zombie_walk_frame_callback(entity ent) {
// // print(ftos(zombie_ent.cur_fg_frame_callback_is_end_frame));
// print("\n");
if(get_zombie_walk_is_footstep(ent.frame)) {
if(get_zombie_walk_is_footstep(zombie_ent.frame)) {
if(random() < 0.5) {
sound(self, 5, "sounds/zombie/s0.wav", 1, ATTN_NORM);
}
@ -320,72 +235,36 @@ void zombie_walk_frame_callback(entity ent) {
}
}
if(zombie_ent.cur_fg_frame_callback_is_end_frame) {
// print("At final frame, restarting walk\n");
zombie_ent.fg_walk();
}
// TODO - If at final frame, call fg_walk again...
// AI_Zombie zombie_ent = (AI_Zombie) ent;
// zombie_ent.fg_walk();
// ent.fg_walk();
// Ideal animation velocity:
// float dist_per_frame = get_zombie_walk_dist_for_frame(zombie_ent.frame);
// float speed = dist_per_frame / zombie_ent.cur_anim_frametime;
// // Convert that to velocity at think_time:
// zombie_ent.do_walk_to_goal(speed);
};
// Called once per ent.think invocation
void zombie_walk_think_callback(entity ent) {
// print("Think called\n");
void zombie_walk_think_callback() {
print("Think called\n");
// print("Think called for frame: ");
// print(ftos(ent.frame));
// print("\n");
AI_Zombie zombie_ent = (AI_Zombie) ent;
// zombie_ent.fg_walk();
AI_Zombie zombie_ent = (AI_Zombie) self;
// "::classname" denotes the global ".string classname" field.
entity player_ent = find( world, ::classname, "player");
entity player_ent = find( world, classname, "player");
if(player_ent != world) {
zombie_ent.path_target = player_ent;
ent.enemy = player_ent;
zombie_ent.enemy = player_ent;
}
float n_anim_frames = fabs(zombie_ent.cur_fg_end_frame - zombie_ent.cur_fg_start_frame);
// float dist_per_frame = 5.23125; // qu
// // Ideal animation velocity:
float dist_per_frame = get_zombie_walk_dist_for_frame(zombie_ent.frame);
float time_per_frame = zombie_ent.cur_fg_duration / n_anim_frames;
float speed = dist_per_frame / time_per_frame;
float speed = dist_per_frame / zombie_ent.cur_anim_frametime;
zombie_ent.do_walk_to_goal(speed);
};
void create_framegroups() {
// walk0 37 52
// walk1 53 66
// walk3 67 82
// jog0 116 129
// run0 78 85
// idle0 0 12
// attack0 86 90
// attack1 91 96
// death0 123 133
// death1 134 138
// death2 139 148
// In blender, it's frames 38 to 53, inclusive
init_framegroup( 38, 53, 1.0, zombie_walk_frame_callback, zombie_walk_think_callback, fg_zombie_walk_0);
init_framegroup( 53, 66, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_walk_1);
init_framegroup( 67, 82, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_walk_2);
init_framegroup( 116, 129, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_jog_0);
init_framegroup( 78, 85, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_run_0);
init_framegroup( 0, 12, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_idle_0);
init_framegroup( 86, 90, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_attack_0);
init_framegroup( 91, 96, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_attack_1);
init_framegroup( 123, 133, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_die_0);
init_framegroup( 134, 138, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_die_1);
init_framegroup( 139, 148, 10.0, (void(entity)) SUB_Null, (void(entity)) SUB_Null, fg_zombie_die_2);
};
@ -427,7 +306,7 @@ void(vector org) AI_Zombie::init = {
// ---------------------------------
this.think_delta_time = 0.1; // 10x per second
this.nextthink = time + this.think_delta_time;
this.cur_fg_start_time = -1;
// this.cur_fg_start_time = -1;
}
@ -437,116 +316,93 @@ void(vector org) AI_Zombie::init = {
// }
void () AI_Zombie::fg_walk = {
this.set_framegroup(fg_zombie_walk_0);
this.set_framegroup_duration(fg_zombie_walk_0.duration * (1.0 + 3.0 * random()));
// print("Set anim duration to: ");
// print(ftos(this.cur_fg_duration));
// print("\n");
}
void () AI_Zombie::fg_die = {
// TODO - Execute -- zombie_die_0
}
void () AI_Zombie::fg_attack = {
// TODO - Execute an attack framegroup
}
void () AI_Zombie::fg_idle = {
// TODO - Execute the idle framegroup
}
// void () AI_Zombie::traverse = {
// this.traversal_idx; // Can't use pointers... but can use index!
// this.traversal_state;
// this.traversal_substate;
// // TODO - Check traversal type, check if this class knows how to perform it.
// float traversal_idx = 0;
// // TODO - Once we know traversal type, the AI_Zombie class will know which think_callback to use
// // TODO - Set up a test scene on the map, set up a test traversal, have dummy zombie play the test traversal
// this.traversal_callback = zombie_traversal_hop_barricade_think;
// void (entity ent) zombie_hop_barricade_traveral_think = {
// AI_Zombie zombie_ent = (AI_Zombie) ent;
// // TODO - need to initialize traversal state and traversal substate
// // Initialize state
// if(ent.traversal_state < 0) {
// ent.traversal_state = 0;
// ent.traversal_substate = 0;
// }
// // State 0 -- Start hopping, start playing anim, lerp towards
// // TODO - How to adjust animation speed scale?
// // TODO - How best to control animation from here?
// if(ent.traversal_state == 0) {
// // TODO - Somehow start playing animation?
// }
// // ...
// // On final state, clear traversal
// else {
// ent.traversal_state = -1;
// // TODO - Revert to zombie original behavior, fg_walk?
// zombie_ent.fg_walk();
// }
// };
// // TODO - Implement think logic for hop barricade traversal
// // Each traversal has a different think function implementation...
// // When I call zombie.traverse(), I need some way of telling the zombie which barricade to traverse
// // Maybe traverse has a traversal_idx as its argument?
// // zombie_ent.traverse(traversal_idx);
// // That tells the zombie to perform this traversal
// // And the traversal think function will be stored in the struct
// #define MYFUNC(DUMMY, FN, I) int FN(void) { return I; }
// #define GENFUNCS(...) \
// P99_FOR(, P99_NARG(__VA_ARGS__), P00_IGN, MYFUNC, __VA_ARGS__) \
// int (*function_table)(void)[] = { __VA_ARGS__ }
// GENFUNCS(toto, hui, gogo);
void() test_frame_callback = {
// print("Frame Callback: ", self.classname, "\n");
AI_Zombie ent = self;
print("Frame Callback. Frame: ", ftos(ent.frame), " anim frame_idx: ", ftos(ent.cur_anim_frame_idx), "\n");
}
void() test_new_ent = {
create_framegroups();
makevectors(self.v_angle);
// AI_Chase test_ent = spawn(AI_Chase);
AI_Zombie zombie = spawn(AI_Zombie);
zombie.init(self.origin + v_forward * 100);
zombie.fg_walk();
// TODO - If riser, Immediately set frmae to below ground frame
zombie.frame = get_anim_frame_zombie_rise(0);
zombie.play_anim(get_anim_frame_zombie_rise, get_anim_length_zombie_rise(), ANIM_STOP_TYPE_NEXT_ANIM);
zombie.queue_anim(get_anim_frame_zombie_walk1, get_anim_length_zombie_walk1(), ANIM_STOP_TYPE_LOOP);
zombie.frame_callback = zombie_walk_frame_callback;
zombie.think_callback = zombie_walk_think_callback;
// zombie.play_anim(get_anim_frame_zombie_walk1, get_anim_length_zombie_walk1(), ANIM_STOP_TYPE_NEXT_ANIM);
// zombie.queue_anim(get_anim_frame_zombie_rise, get_anim_length_zombie_rise(), ANIM_STOP_TYPE_LOOP);
// somefunc();
// framegroup_registry zombie_th_reg;
// // framegroup_registry crawler_th_reg;
// // framegroup_registry dog_th_reg;
// framegroup zombie_attack_0;
// // framegroup zombie_attack_1;
// // framegroup zombie_idle_0;
// // framegroup zombie_die_0;
// // framegroup zombie_die_1;
// // framegroup zombie_die_2;
// // framegroup zombie_walk_0;
// // framegroup zombie_walk_1;
// // framegroup zombie_walk_2;
// // framegroup zombie_jog_0;
// // framegroup zombie_run_1;
// // framegroup zombie_run_2;
// // framegroup zombie_run_3;
// create_framegroup( zombie_attack_0, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_attack_1, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_idle_0, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_die_0, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_die_1, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_die_2, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_walk_0, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_walk_1, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_walk_2, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_jog_0, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_run_1, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_run_2, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // create_framegroup( zombie_run_3, 0, 10, 5.0, SUB_Null, SUB_Null, SUB_Null);
// // zombie_th_reg.fg_attack.num_framegroups = 2;
// print(strcat("Num before:",ftos(zombie_th_reg.fg_attack.num_framegroups),"\n"));
// register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num after:",ftos(zombie_th_reg.fg_attack.num_framegroups),"\n"));
// zombie_th_reg.fg_attack = register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num before:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// zombie_th_reg.fg_attack = register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num after:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// print(strcat("Num before:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// zombie_th_reg.fg_attack = register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num after:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// print(strcat("Num before:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// zombie_th_reg.fg_attack = register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num after:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// print(strcat("Num before:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// zombie_th_reg.fg_attack = register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num after:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// print(strcat("Num before:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// zombie_th_reg.fg_attack = register_framegroup( zombie_th_reg.fg_attack, zombie_attack_0);
// print(strcat("Num after:",ftos(zombie_th_reg.fg_attack.num_framegroups)));
// print(ftos(result));
// print("\n");
// result = register_framegroup( &(zombie_th_reg.fg_attack), &zombie_attack_1);
// print(ftos(result));
// print("\n");
// result = register_framegroup( &(zombie_th_reg.fg_idle), &zombie_idle_0);
// print(ftos(result));
// print("\n");
// result = register_framegroup( &(zombie_th_reg.fg_die), &zombie_die_0);
// print(ftos(result));
// print("\n");
// result = register_framegroup( &(zombie_th_reg.fg_die), &zombie_die_1);
// print(ftos(result));
// print("\n");
// TODO - Set frametime to something random?
zombie.cur_anim_frametime = 0.1;
// zombie.next_anim_frametime = 0.05 + 0.1 * random();
zombie.next_anim_frametime = 0.13;
// zombie.next_anim_frametime = 0.1;
};

View file

@ -80,15 +80,14 @@ void sv_load_navmesh_data() {
return;
}
//First line contains navmesh file semver
// First line contains navmesh file semver
string nav_file_version = fgets(file);
//Next line contains vertex count
string line = fgets(file);
float vert_count = stof(line);
// Next line contains vertex count
float vert_count = stof(fgets(file));
if(vert_count > sv_navmesh_verts.length) {
print("Error: navmesh file \"",filepath,"\" has an invalid vert count. (" , line, " > ", ftos(sv_navmesh_verts.length),").\n");
print("Error: navmesh file \"",filepath,"\" has an invalid vert count. (" , vert_count, " > ", ftos(sv_navmesh_verts.length),").\n");
fclose(file);
return;
}
@ -96,21 +95,15 @@ void sv_load_navmesh_data() {
//The navmesh appears to be valid (we shouldn't need to clear the current navmesh values)
sv_navmesh_vert_count = vert_count;
//Temp vector to assign component-wise
vector temp;
//Reading all of the vertex positions
for(float i = 0; i < sv_navmesh_vert_count; i++) {
line = fgets(file);
temp = stov(line);
sv_navmesh_verts[i].pos.x = temp.x;
sv_navmesh_verts[i].pos.y = temp.y;
sv_navmesh_verts[i].pos.z = temp.z;
sv_navmesh_verts[i].pos = stov(fgets(file));
}
//Next line contains the number of polygons
sv_navmesh_poly_count = stof(fgets(file));
print("Polygon count: ", ftos(sv_navmesh_poly_count), "\n");
//The next lines are each polygon
for(float i = 0; i < sv_navmesh_poly_count; i++) {
@ -122,10 +115,7 @@ void sv_load_navmesh_data() {
sv_navmesh_polies[i].verts[2] = stof(fgets(file));
sv_navmesh_polies[i].verts[3] = stof(fgets(file));
// Get polygon center
temp = stov(fgets(file));
sv_navmesh_polies[i].center.x = temp.x;
sv_navmesh_polies[i].center.y = temp.y;
sv_navmesh_polies[i].center.z = temp.z;
sv_navmesh_polies[i].center = stov(fgets(file));
// Get link count
sv_navmesh_polies[i].connected_polies_count = stof(fgets(file));
// Get links
@ -151,7 +141,7 @@ void sv_load_navmesh_data() {
// If navmesh file version 0.0.0, no traversals
// FIXME - Need a better way to do this..
if(strcmp(nav_file_version, "0.0.0") != 0) {
if(nav_file_version != "0.0.0") {
// Get connected traversals
sv_navmesh_polies[i].connected_traversals_count = stof(fgets(file));
for(float j = 0; j < sv_navmesh_polies[i].connected_traversals_count; j++) {
@ -160,8 +150,11 @@ void sv_load_navmesh_data() {
}
//Get polygon doortarget
sv_navmesh_polies[i].doortarget = fgets(file);
//Get entrance edge
sv_navmesh_polies[i].entrance_edge = stoi(fgets(file));
// If v0.0.0, throw away entrance edge (legacy feature not supported)
if(nav_file_version == "0.0.0") {
// sv_navmesh_polies[i].entrance_edge = stoi(fgets(file));
fgets(file);
}
}
// -----------------------------------------------------------------------
@ -169,27 +162,15 @@ void sv_load_navmesh_data() {
// -----------------------------------------------------------------------
// If navmesh file version 0.0.0, no traversals
// FIXME - Need a better way to do this..
if(strcmp(nav_file_version, "0.0.0") != 0) {
if(nav_file_version != "0.0.0") {
//Next line contains the number of traverals
sv_navmesh_traversal_count = stof(fgets(file));
//The next lines are each traversal
for(float i = 0; i < sv_navmesh_traversal_count; i++) {
line = fgets(file);
temp = stov(line);
sv_navmesh_traversals[i].start_pos.x = temp.x;
sv_navmesh_traversals[i].start_pos.y = temp.y;
sv_navmesh_traversals[i].start_pos.z = temp.z;
line = fgets(file);
temp = stov(line);
sv_navmesh_traversals[i].midpoint_pos.x = temp.x;
sv_navmesh_traversals[i].midpoint_pos.y = temp.y;
sv_navmesh_traversals[i].midpoint_pos.z = temp.z;
line = fgets(file);
temp = stov(line);
sv_navmesh_traversals[i].end_pos.x = temp.x;
sv_navmesh_traversals[i].end_pos.y = temp.y;
sv_navmesh_traversals[i].end_pos.z = temp.z;
sv_navmesh_traversals[i].start_pos = stov(fgets(file));
sv_navmesh_traversals[i].midpoint_pos = stov(fgets(file));
sv_navmesh_traversals[i].end_pos = stov(fgets(file));
sv_navmesh_traversals[i].angle = stof(fgets(file));
sv_navmesh_traversals[i].use_midpoint = stof(fgets(file));
sv_navmesh_traversals[i].end_poly = stof(fgets(file));
@ -272,22 +253,19 @@ float sv_navmesh_dist_to_poly(vector pos, float poly_index)
//Returns polygon that pos is inside of.
//If we are in no polygon, returns a polygon whose edge we are sufficiently close to (might be in but are not due to a small error)
//Otherwise, returns -1
float sv_navmesh_get_containing_poly(vector pos)
{
float sv_navmesh_get_containing_poly(vector pos) {
//Get nearest polygon, and check if pos is in that polygon
float closest_poly = -1;
float closest_poly_dist = 1000000;
//In reality, there's no need to try ALL polygons, the one we are in should be within the nearest 10 or so, but checking all to be safe
for(float i = 0; i < sv_navmesh_poly_count; i++)
{
for(float i = 0; i < sv_navmesh_poly_count; i++) {
//Check if we are sufficiently close enough to polygon i in the z-axis
//Current method of checking: if pos is within 30 qu of polygon z-axis bounds
float poly_min_z = sv_navmesh_verts[sv_navmesh_polies[i].verts[0]].pos.z;
float poly_max_z = poly_min_z;
for(float j = 0; j < sv_navmesh_polies[i].vert_count; j++)
{
for(float j = 0; j < sv_navmesh_polies[i].vert_count; j++) {
float vert_z = sv_navmesh_verts[sv_navmesh_polies[i].verts[j]].pos.z;
poly_min_z = min(poly_min_z, vert_z);
poly_max_z = max(poly_max_z, vert_z);
@ -295,15 +273,12 @@ float sv_navmesh_get_containing_poly(vector pos)
// If we NOT between [min - 30, max + 30], skip this polygon
// if(fabs(sv_navmesh_polies[0].center.z - pos.z) >= 30)
if(pos.z < poly_min_z - 30 || pos.z > poly_max_z + 30)
{
if(pos.z < poly_min_z - 30 || pos.z > poly_max_z + 30) {
continue;
}
//Check if we are in polygon i
if(sv_navmesh_is_inside_poly(pos,i))
{
if(sv_navmesh_is_inside_poly(pos,i)) {
// NOTE - Because is_inside_poly only checks if we are in it on X & Y axes,
// NOTE we may have found a polygon on a floor below us or above us.
// NOTE However, this may not be an issue. A polygon on a floor below
@ -317,25 +292,17 @@ float sv_navmesh_get_containing_poly(vector pos)
//If we are not in polygon i, check if we arse very close to one of its edges
float dist = sv_navmesh_dist_to_poly(pos,i);
if(dist >= 0)
{
if(dist < closest_poly_dist)
{
closest_poly = i;
closest_poly_dist = dist;
}
if(dist >= 0 && dist < closest_poly_dist) {
closest_poly = i;
closest_poly_dist = dist;
}
}
//============================================
/*if(closest_poly == -1)
{
/*if(closest_poly == -1) {
print("Ent pos is not in or near any polygons.\n");
}
else
{
else {
print("Ent pos is near but not in poly: closest_poly.\n");
}*/

View file

@ -971,11 +971,22 @@ float sv_navmesh_pathfind_start(float start_poly, float goal_poly, vector start_
// print(ftos(neighbor_poly));
// print(".\n");
// ----------------------------------------------------------------
// Door check
// ----------------------------------------------------------------
// NOTE - If polygon's door hasn't been opened, don't consider it.
// NOTE - This check isn't done in the navmesh editor.
// If this polygon references a door:
if(sv_navmesh_polies[neighbor_poly].doortarget != "") {
entity door;
door = find(world, wayTarget, sv_navmesh_polies[neighbor_poly].doortarget);
if(door != world) {
// If the referenced door is closed, don't consider this polygon.
if(door.state == STATE_BOTTOM) {
continue;
}
}
}
// ----------------------------------------------------------------

View file

@ -485,20 +485,7 @@ navmesh_pathfind_result *sv_zombie_pathfind_result_aux; // Used as secondary, so
//=================================================================
//========================== AI defs ==============================
var struct framegroup {
float start_frame;
float end_frame;
float duration;
// void(entity) start_callback; // Called at the first frame of the range
void(entity) frame_callback; // Called at every frame of the range (after start / finish callbacks)
// void(entity) end_callback; // Called at the last frame of the range
// FIXME - Increasing the size of this framegroup by one more function pointer causes chaos...
// I assume I run out of space to pass framegroups along as arguments at any point...
// It starts to write into weird memory locations, like overriding self / world... things just break down.
// TODO - How can I refactor this to work?
void(entity) think_callback; // Called at each think invocation
// -- Maybe I can get rid of start / end callbacks? idk
// I should think about how I actually plan on using these...
// ----------------------------------------------------------------------
// Animation Definitions
@ -535,54 +522,110 @@ var struct framegroup {
float get_anim_length_##ANIM_NAME## () {float frames[] = {__VA_ARGS__}; return frames.length;};
// ----------------------------------------------------------------------
// TODO - Get rid of start / end callback, add fields to the AI_Chase class that denotes
// TODO - These callbacks will be executed at the same time as frame anyways.
enum anim_stop_type:float {
ANIM_STOP_TYPE_STOP, // Animation stops and freezes at final frame.
ANIM_STOP_TYPE_LOOP, // Animation starts again from the first frame.
ANIM_STOP_TYPE_NEXT_ANIM, // Another animation is played when this one finishes.
};
class AI_Chase : entity {
// framegroup_registry *th_reg;
entity path_target; // If specified, path towards entity
vector path_pos; // Otherwise, path towards location specified
float think_delta_time; // Delta time (seconds) between each `this.think();`
// Regardless of what animation we're playing (or not playing), we have a think callback
virtual void() think_callback = SUB_Null;
// We also have a frame callback that's called each time we reach a new animation frame.
virtual void() frame_callback = SUB_Null;
// ------------------------------
// Current Framegroup value vars
// Animation variables
// ------------------------------
float cur_fg_start_time;
float cur_fg_start_frame;
float cur_fg_end_frame;
float cur_fg_duration;
// void() endanimfunc = SUB_Null;
// virtual void(entity) cur_fg_start_callback = SUB_Null;
virtual void(entity) cur_fg_frame_callback = (void(entity)) SUB_Null;
// virtual void(entity) cur_fg_end_callback = SUB_Null;
virtual void(entity) cur_fg_think_callback = (void(entity)) SUB_Null;
// ------------------------------
// Framegroup state vars
// ------------------------------
float cur_fg_idx; // Counter for checking when the framegroup has been changed
float cur_fg_frame_callback_next_frame;
float cur_fg_frame_callback_is_start_frame; // Is set to true when performing cur_fg_frame_callback for the first framegroup frame
float cur_fg_frame_callback_is_end_frame; // Is set to true when performing cur_fg_frame_callback for the final framegroup frame
// --- Current animation vars ---
virtual float(float) cur_anim_get_frame_func = (float(float)) SUB_Null; // Gets frame number from frame index. Assign to `get_anim_frame_{ANIM_NAME}`, as generated by DEFINE_ANIM
float cur_anim_length; // The length of the current animation. Assign to `get_anim_length_{ANIM_NAME}()`, as generated by DEFINE_ANIM)
float cur_anim_frametime; // Time (in seconds) between frames, 0.1 for 10FPS
anim_stop_type cur_anim_stop_type; // ANIM_STOP_TYPE_STOP, ANIM_STOP_TYPE_LOOP, or ANIM_STOP_TYPE_NEXT_ANIM
// --- Next / Queued animation vars ---
virtual float(float) next_anim_get_frame_func = (float(float)) SUB_Null; // Gets frame number from frame index. Assign to `get_anim_frame_{ANIM_NAME}`, as generated by DEFINE_ANIM
float next_anim_length; // The length of the current animation. Assign to `get_anim_length_{ANIM_NAME}()`, as generated by DEFINE_ANIM)
float next_anim_frametime; // Time (in seconds) between frames, 0.1 for 10FPS
anim_stop_type next_anim_stop_type; // ANIM_STOP_TYPE_STOP or ANIM_STOP_TYPE_LOOP
// --- Current animation state vars ---
float cur_anim_start_time;
float cur_anim_frame_idx; // Counter from 0 to `cur_anim_n_frames`
// ------------------------------
float pathfind_result_idx; // TODO - Need to increment this on instantiation
float pathfind_cur_point_idx;
// Constructor. Called when calling `spawn(AI_Chase);`
virtual void() AI_Chase;
virtual void() think;
virtual void (float duration) set_framegroup_duration;
virtual void (framegroup fgroup) set_framegroup;
virtual void () fg_die = {};
virtual void () fg_walk = {};
virtual void () fg_attack = {};
virtual void () fg_idle = {};
// Stop what you're doing and play this animation immediately.
virtual void(float(float) anim_frame_func, float anim_length, anim_stop_type stop_type) play_anim = {
// Assign Animation Vars
this.cur_anim_get_frame_func = anim_frame_func;
this.cur_anim_length = anim_length;
this.cur_anim_stop_type = stop_type;
this.cur_anim_frametime = 0.1; // TODO - Make this a param?
this.cur_anim_frametime = 0.2; // TODO - Make this a param?
// Reset Animation state Vars
this.cur_anim_start_time = time;
this.cur_anim_frame_idx = 0;
// Adjust think_delta_time if needed
// If we want to play the animation faster than 10FPS, call logic ticks faster so the animation will be played
if(this.cur_anim_frametime < 0.10) {
this.think_delta_time = this.cur_anim_frametime * 0.5;
}
else {
this.think_delta_time = 0.1;
}
this.think_delta_time = this.cur_anim_frametime * 0.5;
this.think_delta_time = this.cur_anim_frametime * 0.1;
// TODO - Do some math and figure out if we need to
// TODO - Thinktime should be adjusted so that
// TOOD - this.thinktime = min(0.1, this.cur_anim_frametime * 0.05);
// TODO - Reset FPS to 10 FPS, (frametime = 0.1)
// TODO - Add a function that allows this to play slower or faster?
};
// Queue up another animation to play when the current one finishes
virtual void(float(float) anim_frame_func, float anim_length, anim_stop_type stop_type) queue_anim {
// This isn't allowed, we can't queue more than one animation.
// Queued animation stop type must be: ANIM_STOP_TYPE_STOP, or ANIM_STOP_TYPE_LOOP
if(stop_type == ANIM_STOP_TYPE_NEXT_ANIM) {
return;
}
// Indicate that we have a next animation queued up the next time the current animation finishes.
this.cur_anim_stop_type = ANIM_STOP_TYPE_NEXT_ANIM;
this.next_anim_get_frame_func = anim_frame_func;
this.next_anim_length = anim_length;
this.next_anim_stop_type = stop_type;
this.next_anim_frametime = 0.1;
this.next_anim_frametime = 0.2; // FIXME - Parameterize this?
};
// virtual void () fg_die = {};
// virtual void () fg_walk = {};
// virtual void () fg_attack = {};
// virtual void () fg_idle = {};
virtual void (float dist) do_walk_to_goal;
};
@ -596,12 +639,21 @@ class AI_Zombie : AI_Chase {
// This should be called explicitly:
virtual void(vector org) init;
virtual void () fg_die;
virtual void () fg_walk;
virtual void () fg_attack;
virtual void () fg_idle;
// virtual void () fg_die;
// virtual void () fg_walk;
// virtual void () fg_attack;
// virtual void () fg_idle;
// static void () setup_frames = {
// // this.zombie_idle_frames = {1,2,3,4,5,6,7,8,9,10,11,12,13};
// // this.cur_frames = zombie_idle_frames;
// // FIXME - I can't figure out a way to assign an array...
// // Even if I convert an animation to a struct, I can't do arbitrary-length arrays...
// // I also can't fill them in in this convenient syntax... it's ridiculous.
// };
};
// AI_Zombie.zombie_idle_frames = {1,2,3,4,5,6,7,8,9,10,11,12,13};
//=================================================================

View file

@ -348,7 +348,8 @@ void() door_trigger_touch =
#ifdef PC
if(cvar("navmesh_edit_mode")) {
if(strcmp(cvar_string("nav_editor_active_door"), self.owner.wayTarget) != 0) {
// if(strcmp(cvar_string("nav_editor_active_door"), self.owner.wayTarget) != 0) {
if(cvar_string("nav_editor_active_door") != self.owner.wayTarget) {
bprint(PRINT_HIGH, "Current Door for navmesh editor set to \"");
bprint(PRINT_HIGH, self.owner.wayTarget); // naiveil (FIXME)
bprint(PRINT_HIGH, "\"\n");

View file

@ -107,6 +107,23 @@ float(float a, float b, float mix) unclamped_lerp =
return (b * mix + a * ( 1 - mix ) );
}
#ifndef PC // FTE has built-ins for min / max
float(float a, float b) min =
{
return a < b ? a : b;
}
float(float a, float b) max =
{
return a > b ? a : b;
}
#endif // PC
// Computes the length of a vector ignoring the Z-component
float (vector ofs) vlen_xy = {
return vlen([ofs.x, ofs.y, 0]);
}
vector(vector a, vector b, float mix) lerpVector =
{

View file

@ -39,7 +39,7 @@ struct navmesh_poly {
float vert_count;
string doortarget; // "" or matches the .wayTarget field of a door entity. Polygon is only used when door is open (i.e. door.state == STATE_BOTTOM)
// TODO - Remove this, will be replaced by traversals
int entrance_edge; // If != -1, specifies which edge index this polygon can be entered from. (0,1,2, or 3)
// int entrance_edge; // If != -1, specifies which edge index this polygon can be entered from. (0,1,2, or 3)
float connected_traversals_count; // How many traversals start in this polygon
float connected_traversals[NAV_MAX_POLY_TRAVERSALS]; // List of traversals that start in this polygon