SERVER Add IQM Zombie Revamp

CLIENT: Add drawing of IQM Zombie Revamp
This commit is contained in:
blubs 2023-08-28 01:11:51 -07:00
parent e463a671d2
commit 1c0f9889d9
8 changed files with 1077 additions and 1158 deletions

View file

@ -571,8 +571,12 @@ void cl_navmesh_editor_draw() {
cl_navmesh_draw_traversal(i);
}
cl_navmesh_draw_test_ent(startent_pos, [1,1,1], [0,1,0], 0.4);
cl_navmesh_draw_test_ent(goalent_pos, [1,1,1], [1,0,0], 0.4);
if(startent_set) {
cl_navmesh_draw_test_ent(startent_pos, [1,1,1], [0,1,0], 0.4);
}
if(goalent_set) {
cl_navmesh_draw_test_ent(goalent_pos, [1,1,1], [1,0,0], 0.4);
}
cl_navmesh_pathfind_draw_result_path();
cl_navmesh_pathfind_draw_result_portals();
@ -2025,8 +2029,10 @@ void cl_navmesh_editor_load_navmesh() {
float(float start, float goal, vector start_pos, vector end_pos) cl_navmesh_pathfind_start;
void cl_navmesh_draw_test_ent(vector pos, vector scale, vector color, float alpha)
{
void cl_navmesh_draw_test_ent(vector pos, vector scale, vector color, float alpha) {
//Assigning the shader as something else so that fte doesn't batch the calls (leading to colors not changing between draw calls)
R_BeginPolygon("debug/wireframe",0);
R_BeginPolygon("debug/solid_nocull",0);

File diff suppressed because it is too large Load diff

View file

@ -1293,8 +1293,9 @@ float sv_navmesh_pathfind(entity from, entity to) {
// We can perform pathfinding logic without losing our current path.
// TODO - In pathfinding, I need a reference to the calling entity to know which traversal types this entity can use
AI_Chase chase_ent = (AI_Chase) from;
navmesh_pathfind_result* res = &(sv_zombie_pathfind_result[chase_ent.pathfind_result_idx]);
// AI_Chase chase_ent = (AI_Chase) from;
// navmesh_pathfind_result* res = &(sv_zombie_pathfind_result[chase_ent.pathfind_result_idx]);
navmesh_pathfind_result* res = &(sv_zombie_pathfind_result[0]); // FIXME
float start = sv_navmesh_get_containing_poly(from.origin);
float goal = sv_navmesh_get_containing_poly(to.origin);

View file

@ -483,168 +483,6 @@ navmesh_pathfind_result *sv_zombie_pathfind_result; // Used as primary. The one
navmesh_pathfind_result *sv_zombie_pathfind_result_aux; // Used as secondary, so we can pathfind without losing current path
#endif // PC
//=================================================================
//========================== AI defs ==============================
// ----------------------------------------------------------------------
// Animation Definitions
// ----------------------------------------------------------------------
// For the animation system, we could define each animation as the start and
// end frame range, but we'd like to be able to compose animations out of
// frame numbers that are not contiguous (e.g. frames=[0,1,5,6,2,3]).
// To pull this off, we need to treat animations as a list of frame numbers.
// For the animation system, we want to pass around pointers to thse arrays so
// that an entity can keep track of the frame list for its current animation.
// However, QC only allows for function pointers. Meaning, that if we want to
// pass around these animations, then we can only pass them around as function
// pointers. Thus, we must create two functions for each frame list, one for
// each thing we would need to do to a read-only array:
// - access an index
// - get the array length
// To pull this off, we'd need to define the following two functions for
// every animation:
// float get_anim_frame_zombie_walk1(float frame_idx) {float frames[] = {38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53}; return frames[frame_idx]};
// float get_anim_length_zombie_walk1(float frame_idx) {float frames[] = {38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53}; return frames.length};
// But, instead of doing that for _every_ animation, the following preprocessor
// directive will generate the above two functions from the single statement:
// DEFINE_ANIM(zombie_walk1, 38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53);
// After the above statement, the functions `get_anim_frame_zombie_walk1` and
// `get_anim_length_zombie_walk1` have been defined and are now available to
// use and pass around.
// ----------------------------------------------------------------------
#define DEFINE_ANIM( ANIM_NAME, ...); \
float get_anim_frame_##ANIM_NAME## (float frame_idx) {float frames[] = {__VA_ARGS__}; return frames[frame_idx];}; \
float get_anim_length_##ANIM_NAME## () {float frames[] = {__VA_ARGS__}; return frames.length;};
// ----------------------------------------------------------------------
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 state;
float substate;
// -----------------------------
// Traversal state vars
// -----------------------------
float cur_traversal_idx;
float cur_traversal_end_time;
float cur_traversal_start_time;
// 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;
// ------------------------------
// Animation variables
// ------------------------------
// --- 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;
// 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;
// Reset Animation state Vars
this.cur_anim_start_time = time;
this.cur_anim_frame_idx = 0;
this.frame = anim_frame_func(0);
};
// 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;
};
// virtual void () fg_die = {};
// virtual void () fg_walk = {};
// virtual void () fg_attack = {};
// virtual void () fg_idle = {};
virtual void (float dist) do_walk_to_goal;
};
class AI_Zombie : AI_Chase {
entity enemy; // If near, attack
// Constructor. Called when calling `spawn(AI_Zombie);`
// virtual void() AI_Zombie = {};
// 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;
// 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};
//=================================================================
//Misc patch definitions
.string teddyremovetarget;

View file

@ -1194,10 +1194,28 @@ float(float modlindex, optional float useabstransforms) skel_create = #263; /* P
Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model.
eg: self.skeletonobject = skel_create(self.modelindex); */
typedef struct {
int sourcemodelindex; /*frame data will be imported from this model, bones must be compatible*/
int reserved;
int firstbone;
int lastbone;
float prescale; /*0 destroys existing data, 1 retains it*/
float scale[4]; /*you'll need to do lerpfrac manually*/
int animation[4];
float animationtime[4];
/*halflife models*/
float subblend[2];
float controllers[5];
} skelblend_t;
float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac) skel_build = #264; /* Part of FTE_CSQC_SKELETONOBJECTS
Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object.
If retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based. */
float(float skel, int numblends, skelblend_t *weights, int structsize) skel_build_ptr = #0:skel_build_ptr; /*
Like skel_build, but slightly simpler. */
float(float skel) skel_get_numbones = #265; /* Part of FTE_CSQC_SKELETONOBJECTS
Retrives the number of bones in the model. The valid range is 1<=bone<=numbones. */
@ -1386,6 +1404,12 @@ string(string key) serverkey = #354; /*
float(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat; /*
Version of serverkey that returns the value as a float (which avoids tempstrings). */
float(string skinfilename, optional string skindata) loadcustomskin = #377;
void(entity e, float skinobj) applycustomskin = #378;
void(float skinobj) releasecustomskin = #379;
__variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC
Allocate an arbitary block of memory */

View file

@ -98,6 +98,58 @@ void() StartFrame =
return;
}
// ------------------------------------------------------------------------
// Debugging IQM SSQC hitmeshes
// ------------------------------------------------------------------------
entity player = find(world, classname, "player");
if(player != world) {
vector player_pos = player.origin;
vector player_angles = player.v_angle;
makevectors(player_angles);
vector view_ofs = '0 0 32';
// traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, 0, player);
// player.dimension_hit = 0;
// traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, 4, player);
traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, MOVE_HITMODEL, player); // 1<<2
// traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, MOVE_EVERYTHING, player); // 1<<5
// traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, MOVE_EVERYTHING | MOVE_HITMODEL, player); // 1<<5
// traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, 0, player);
player.dimension_hit |= HITBOX_DIM_ZOMBIES;
// traceline(player_pos + view_ofs, player_pos + view_ofs + v_forward * 1000, MOVE_OTHERONLY | MOVE_HITMODEL, player);
// print("trace dist: ", ftos(1000 * trace_fraction), "\n");
if(trace_ent.classname == "ai_zombie") {
// entity zombie_ent = (AI_Chase) trace_ent;
// TODO - Modify end limb state...
switch(trace_surface_id) {
case 1:
print("Zombie body\n");
break;
case 2:
print("Zombie head\n");
break;
case 3:
print("Zombie left arm\n");
break;
case 4:
print("Zombie left leg\n");
break;
case 5:
print("Zombie right leg\n");
break;
case 6:
print("Zombie right arm\n");
break;
default:
// print("\n");
break;
}
}
}
// ------------------------------------------------------------------------
if (cl_navmesh_edit_mode) {
return;
}

View file

@ -12,29 +12,6 @@ float(float a, float b) mod;
float(float x) sign;
float(float value, float minValue, float maxValue) wrap;
/*
* clamp
*
* Limits the given value to the given range.
*
* value: A number
*
* minValue: The minimum value of the range
*
* maxValue: The maximum value of the range
*
* Returns: A number within the given range.
*/
float(float value, float minValue, float maxValue) clamp = {
if (value < minValue) {
return minValue;
}
else if (value > maxValue) {
return maxValue;
}
return value;
};
/*
* mod
@ -125,12 +102,6 @@ float (vector ofs) vlen_xy = {
}
vector(vector a, vector b, float mix) lerpVector =
{
if (mix <= 0) return a;
if (mix >= 1) return b;
return (b * mix + a * ( 1 - mix ) );
}
// for a relaxing lerp: hermite lerp.
float(float a, float b, float mix) lerpHermite =

View file

@ -252,4 +252,40 @@ vector(vector a, vector b, float mix) lerp_vector {
vector lerp_vector_bezier(vector a, vector b, vector c, float mix) {
return lerp_vector(lerp_vector(a,b,mix), lerp_vector(b,c,mix), mix);
}
}
/*
* clamp
*
* Limits the given value to the given range.
*
* value: A number
*
* minValue: The minimum value of the range
*
* maxValue: The maximum value of the range
*
* Returns: A number within the given range.
*/
float(float value, float minValue, float maxValue) clamp = {
if (value < minValue) {
return minValue;
}
else if (value > maxValue) {
return maxValue;
}
return value;
};
vector(vector a, vector b, float mix) lerpVector =
{
if (mix <= 0) return a;
if (mix >= 1) return b;
return (b * mix + a * ( 1 - mix ) );
}
// Entity type identifiers when fields are synced from SSQC to CSQC
// TODO - Generalize this to other AI_Chase types?
#define ENT_TYPE_ZOMBIE 5