mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
45a81cf57b
Cleaned up strig handling functions in game DLLs. Refactored ThrowGib(), ThrowHead() and ThrowDebris() functions in missionpack DLL.
707 lines
17 KiB
C
707 lines
17 KiB
C
#if defined(_DEBUG) && defined(_Z_TESTMODE)
|
|
|
|
#include "g_local.h"
|
|
|
|
/*=========================================================================
|
|
Local functions.
|
|
=========================================================================*/
|
|
anim_data_t *anim_player_create(edict_t *monster);
|
|
void anim_player_think(edict_t *anim_player);
|
|
|
|
void update_directions(anim_data_t *data);
|
|
void update_frame_buffer(anim_data_t *data);
|
|
void calculate_buffer_actuals(anim_data_t *data);
|
|
|
|
/*=========================================================================
|
|
Local variables.
|
|
=========================================================================*/
|
|
#define MAX_ANIMATIONS 10
|
|
|
|
anim_data_t *animations[MAX_ANIMATIONS]; //record of all animations
|
|
int animations_count = 0;
|
|
|
|
/**************************************************************************
|
|
Misc routines.
|
|
**************************************************************************/
|
|
qboolean cut_up_string(char **str, char **clipping)
|
|
{
|
|
char *end;
|
|
size_t clippingSize;
|
|
|
|
while (**str == ' ')
|
|
(*str)++;
|
|
|
|
if (**str == '\0')
|
|
{
|
|
*clipping = NULL;
|
|
return false;
|
|
}
|
|
|
|
end = (*str) + 1;
|
|
while (*end != '\0')
|
|
{
|
|
if (*end == ' ')
|
|
{
|
|
*end = '\0';
|
|
end++;
|
|
break;
|
|
}
|
|
end++;
|
|
}
|
|
|
|
clippingSize = strlen(*str)+1;
|
|
*clipping = gi.TagMalloc(clippingSize, TAG_LEVEL);
|
|
Com_strcpy (*clipping, clippingSize, *str);
|
|
|
|
*str = end;
|
|
|
|
return true;
|
|
}
|
|
|
|
edict_t *find_targetname(char *targetname)
|
|
{
|
|
int i;
|
|
|
|
for (i=0;i<globals.num_edicts;i++)
|
|
{
|
|
if (!g_edicts[i].targetname)
|
|
continue;
|
|
|
|
if (Q_stricmp(g_edicts[i].targetname, targetname) == 0)
|
|
return g_edicts + i;
|
|
}
|
|
|
|
gi.dprintf("name <%s> not found\n", targetname);
|
|
return NULL;
|
|
}
|
|
|
|
static edict_t *the_client = NULL;
|
|
|
|
edict_t *find_client(void)
|
|
{
|
|
int i;
|
|
|
|
if (the_client)
|
|
return the_client;
|
|
|
|
if (!maxclients->value)
|
|
return NULL;
|
|
|
|
for (i=1;i<globals.num_edicts;i++)
|
|
{
|
|
if (!g_edicts[i].inuse)
|
|
continue;
|
|
|
|
if (g_edicts[i].client)
|
|
{
|
|
the_client = g_edicts + i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return the_client;
|
|
}
|
|
|
|
/**************************************************************************
|
|
Exported aim correction routine.
|
|
**************************************************************************/
|
|
anim_data_t *find_monster_animator(edict_t *monster)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
|
|
if (anim->monster == monster)
|
|
return anim;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
qboolean anim_player_correct_aim(edict_t *self, vec3_t aim)
|
|
{
|
|
anim_data_t *anim;
|
|
|
|
if (self->extra_data != animations)
|
|
return false;
|
|
|
|
anim = find_monster_animator(self);
|
|
if (!anim)
|
|
return false;
|
|
|
|
VectorCopy(anim->v_aim, aim);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
Methods and events for creating animation player edict and for coaxing
|
|
monsters to behave correctly.
|
|
**************************************************************************/
|
|
|
|
/*=========================================================================
|
|
Replacement black monster events.
|
|
=========================================================================*/
|
|
void no_pain(edict_t *self, edict_t *other, float kick, int damage)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void no_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void no_happen(edict_t *self)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*=========================================================================
|
|
Construction methods.
|
|
=========================================================================*/
|
|
anim_data_t *anim_data_create(edict_t *monster)
|
|
{
|
|
anim_data_t *data;
|
|
|
|
if (!z_frame_get_sequence(monster->classname))
|
|
return NULL;
|
|
|
|
data = gi.TagMalloc(sizeof(anim_data_t), TAG_LEVEL);
|
|
|
|
data->monster = monster;
|
|
monster->extra_data = animations;
|
|
|
|
data->monster_frames[0].aifunc = NULL;
|
|
data->monster_frames[0].dist = 0.0;
|
|
data->monster_frames[0].thinkfunc = NULL;
|
|
|
|
data->monster_move.firstframe = 0;
|
|
data->monster_move.lastframe = 0;
|
|
data->monster_move.frame = data->monster_frames;
|
|
data->monster_move.endfunc = NULL;
|
|
|
|
data->monster_sequences = z_frame_get_sequence(monster->classname);
|
|
|
|
data->current_sequence = 0;
|
|
data->current_frame = 0;
|
|
data->last_dist = 0.0;
|
|
data->moving_forward = true;
|
|
|
|
data->paused = true;
|
|
data->stationary = true;
|
|
data->frame_events = false;
|
|
data->active = true;
|
|
|
|
data->facing = DIR_FIXED;
|
|
data->aim = DIR_FIXED;
|
|
|
|
return data;
|
|
}
|
|
|
|
anim_data_t *anim_player_create(edict_t *monster)
|
|
{
|
|
anim_data_t *data;
|
|
edict_t *anim_player;
|
|
|
|
data = anim_data_create(monster);
|
|
if (!data)
|
|
return data;
|
|
|
|
monster->monsterinfo.currentmove = &(data->monster_move);
|
|
monster->pain = no_pain;
|
|
monster->die = no_die;
|
|
monster->monsterinfo.stand = no_happen;
|
|
monster->monsterinfo.walk = no_happen;
|
|
monster->monsterinfo.run = no_happen;
|
|
monster->monsterinfo.dodge = NULL;
|
|
monster->monsterinfo.attack = no_happen;
|
|
monster->monsterinfo.melee = NULL;
|
|
monster->monsterinfo.sight = NULL;
|
|
monster->monsterinfo.idle = NULL;
|
|
|
|
anim_player = G_Spawn();
|
|
anim_player->extra_data = data;
|
|
anim_player->think = anim_player_think;
|
|
anim_player->nextthink = level.time + 0.1;
|
|
|
|
return data;
|
|
}
|
|
|
|
/*=========================================================================
|
|
Animation player behaviour routines.
|
|
=========================================================================*/
|
|
void advance_anim_frame(anim_data_t *anim, int count)
|
|
{
|
|
mmove_t *seq;
|
|
|
|
anim->moving_forward = (count < 0)? false : true;
|
|
|
|
anim->current_frame += count;
|
|
if (anim->current_sequence)
|
|
{
|
|
seq = anim->monster_sequences[anim->current_sequence - 1];
|
|
anim->current_frame += seq->lastframe - seq->firstframe + 1;
|
|
anim->current_frame %= seq->lastframe - seq->firstframe + 1;
|
|
}
|
|
}
|
|
|
|
void anim_player_think(edict_t *anim_player)
|
|
{
|
|
anim_data_t *data;
|
|
|
|
data = (anim_data_t *)anim_player->extra_data;
|
|
|
|
update_directions(data);
|
|
|
|
if (!data->paused)
|
|
advance_anim_frame(data, 1);
|
|
calculate_buffer_actuals(data);
|
|
update_frame_buffer(data);
|
|
|
|
anim_player->nextthink = level.time + 0.1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
Rountines to make monster follow the frame buffer, ideal_yaw and
|
|
ideal_aim.
|
|
**************************************************************************/
|
|
|
|
void update_directions(anim_data_t *data)
|
|
{
|
|
edict_t *client = find_client();
|
|
vec3_t ang;
|
|
vec3_t point;
|
|
|
|
switch(data->facing)
|
|
{
|
|
case DIR_AT_CLIENT:
|
|
VectorSubtract(client->s.origin, data->monster->s.origin, point);
|
|
VectorNormalize(point);
|
|
vectoangles(point, ang);
|
|
data->monster->ideal_yaw = ang[YAW];
|
|
break;
|
|
case DIR_PARA_CLIENT:
|
|
data->monster->ideal_yaw = client->client->v_angle[YAW];
|
|
break;
|
|
}
|
|
|
|
switch(data->aim)
|
|
{
|
|
case DIR_AT_CLIENT:
|
|
VectorSubtract(client->s.origin, data->monster->s.origin, point);
|
|
VectorNormalize(point);
|
|
vectoangles(point, data->v_aim);
|
|
break;
|
|
case DIR_PARA_CLIENT:
|
|
AngleVectors(client->client->v_angle, data->v_aim, NULL, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Animator ai routine to move and track yaw.
|
|
=========================================================================*/
|
|
void ai_animator(edict_t *self, float dist)
|
|
{
|
|
if (dist != 0.0)
|
|
M_walkmove (self, self->s.angles[YAW], dist);
|
|
|
|
M_ChangeYaw(self);
|
|
}
|
|
|
|
/*=========================================================================
|
|
Routine to load the current actual_* data into the frame buffer.
|
|
=========================================================================*/
|
|
void update_frame_buffer(anim_data_t *data)
|
|
{
|
|
// defaults
|
|
data->monster_frames[0].aifunc = NULL;
|
|
data->monster_frames[0].dist = 0.0;
|
|
data->monster_frames[0].thinkfunc = NULL;
|
|
|
|
data->monster_move.firstframe = data->actual_frame;
|
|
data->monster_move.lastframe = data->actual_frame;
|
|
|
|
if (data->last_actual_frame != data->actual_frame)
|
|
{
|
|
if (!data->stationary)
|
|
{
|
|
if (data->moving_forward)
|
|
{
|
|
data->monster_frames[0].dist =
|
|
data->actual_sequence->frame[data->actual_sequence_idx].dist;
|
|
data->last_dist = data->monster_frames[0].dist;
|
|
}
|
|
else
|
|
{
|
|
data->monster_frames[0].dist = -data->last_dist;
|
|
data->last_dist = data->actual_sequence->frame[data->actual_sequence_idx].dist;
|
|
}
|
|
data->monster_frames[0].aifunc = ai_animator;
|
|
}
|
|
|
|
if (data->frame_events)
|
|
data->monster_frames[0].thinkfunc =
|
|
data->actual_sequence->frame[data->actual_sequence_idx].thinkfunc;
|
|
}
|
|
|
|
data->last_actual_frame = data->actual_frame;
|
|
}
|
|
|
|
/**************************************************************************
|
|
Routines to calculate actual frames to show.
|
|
**************************************************************************/
|
|
int get_total_frame_count(anim_data_t *data)
|
|
{
|
|
int total_frames = 0;
|
|
mmove_t **seq;
|
|
|
|
seq = data->monster_sequences;
|
|
while(*seq)
|
|
{
|
|
total_frames += (*seq)->lastframe - (*seq)->firstframe + 1;
|
|
seq++;
|
|
}
|
|
|
|
return total_frames;
|
|
}
|
|
|
|
/*=========================================================================
|
|
Frame selection routine. Note that sequence 0 has the special meaning of
|
|
cycling through all sequences on after another.
|
|
This routine sets the actual_frame and actual_sequence from current_frame
|
|
and current_sequence.
|
|
Assumptions:
|
|
- current_sequence = 0, means all frames with current_frame looping
|
|
- current_sequence > 0, a valid sequence and valid current_frame
|
|
=========================================================================*/
|
|
void calculate_buffer_actuals(anim_data_t *data)
|
|
{
|
|
int seq_frames, idx;
|
|
|
|
if (data->current_sequence)
|
|
{
|
|
data->actual_sequence = data->monster_sequences[data->current_sequence - 1];
|
|
data->actual_sequence_idx = data->current_frame;
|
|
/*data->actual_frame %= actual_sequence->lastframe -
|
|
actual_sequence->firstframe + 1;*/
|
|
}
|
|
else
|
|
{
|
|
data->current_frame %= get_total_frame_count(data);
|
|
data->actual_sequence_idx = data->current_frame;
|
|
|
|
idx = 0;
|
|
data->actual_sequence = data->monster_sequences[idx];
|
|
seq_frames = data->actual_sequence->lastframe -
|
|
data->actual_sequence->firstframe + 1;
|
|
while(data->actual_sequence_idx + 1 > seq_frames)
|
|
{
|
|
idx++;
|
|
data->actual_sequence_idx -= seq_frames;
|
|
data->actual_sequence = data->monster_sequences[idx];
|
|
seq_frames = data->actual_sequence->lastframe -
|
|
data->actual_sequence->firstframe + 1;
|
|
}
|
|
}
|
|
|
|
data->actual_frame = data->actual_sequence->firstframe +
|
|
data->actual_sequence_idx;
|
|
}
|
|
|
|
/**************************************************************************
|
|
Console command routines.
|
|
**************************************************************************/
|
|
|
|
void anim_player_report(char *targetname, char *description, qboolean on)
|
|
{
|
|
gi.dprintf("%s %s ", targetname, description);
|
|
if (on)
|
|
gi.dprintf("ON\n");
|
|
else
|
|
gi.dprintf("OFF\n");
|
|
}
|
|
|
|
/*=========================================================================
|
|
Advances count frames in the current sequence
|
|
=========================================================================*/
|
|
void anim_player_advance_frame(int count)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
advance_anim_frame(anim, count);
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Advances count sequences.
|
|
=========================================================================*/
|
|
void anim_player_advance_sequence(int count)
|
|
{
|
|
anim_data_t *anim;
|
|
int i, tcount;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
tcount = count;
|
|
|
|
while(tcount > 0)
|
|
{
|
|
tcount--;
|
|
(anim->current_sequence)++;
|
|
if (!anim->monster_sequences[anim->current_sequence - 1])
|
|
anim->current_sequence = 0;
|
|
}
|
|
|
|
while(tcount < 0)
|
|
{
|
|
(anim->current_sequence)--;
|
|
if (anim->current_sequence < 0)
|
|
{
|
|
anim->current_sequence = 0;
|
|
while(anim->monster_sequences[anim->current_sequence])
|
|
(anim->current_sequence)++;
|
|
}
|
|
tcount++;
|
|
}
|
|
anim->current_frame = 0;
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Set facing flag.
|
|
=========================================================================*/
|
|
void anim_player_set_facing(anim_dir_t facing)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
anim->facing = facing;
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Set aim flag.
|
|
=========================================================================*/
|
|
void anim_player_set_aim(anim_dir_t aim)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
anim->aim = aim;
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Toggles current monster event playing.
|
|
=========================================================================*/
|
|
void anim_player_events(void)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
anim->frame_events = !anim->frame_events;
|
|
|
|
anim_player_report(anim->monster->targetname, "frame events",
|
|
anim->frame_events);
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Toggles current monster stationary.
|
|
=========================================================================*/
|
|
void anim_player_still(void)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
anim->stationary = !anim->stationary;
|
|
|
|
anim_player_report(anim->monster->targetname, "stationary",
|
|
anim->stationary);
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Toggles current monster pause.
|
|
=========================================================================*/
|
|
void anim_player_pause(void)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
if (!anim->active)
|
|
continue;
|
|
|
|
anim->paused = !anim->paused;
|
|
|
|
anim_player_report(anim->monster->targetname, "pause",
|
|
anim->paused);
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Captures a monster or sets the monster current if already captured.
|
|
=========================================================================*/
|
|
qboolean anim_player_capture(char *targetname)
|
|
{
|
|
edict_t *ent;
|
|
int i;
|
|
anim_data_t *anim;
|
|
|
|
//make sure we don't already have this one
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
if (Q_stricmp(animations[i]->monster->targetname, targetname) == 0)
|
|
{
|
|
gi.dprintf("Target <%s> already captured\n", targetname);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//make sure we have room to hold the animation reference.
|
|
if (animations_count == MAX_ANIMATIONS)
|
|
{
|
|
gi.dprintf("Maximum of %d animations already used\n", MAX_ANIMATIONS);
|
|
return false;
|
|
}
|
|
|
|
ent = find_targetname(targetname);
|
|
|
|
if (!ent)
|
|
return false;
|
|
|
|
anim = anim_player_create(ent);
|
|
|
|
if (anim)
|
|
{
|
|
animations[animations_count++] = anim;
|
|
gi.dprintf("Target <%s> captured\n", targetname);
|
|
}
|
|
else
|
|
gi.dprintf("Target <%s> NOT captured\n", targetname);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*=========================================================================
|
|
Toggles current monster stationary.
|
|
=========================================================================*/
|
|
void anim_player_set_active(char *targetname, qboolean active)
|
|
{
|
|
anim_data_t *anim;
|
|
int i;
|
|
|
|
for (i=0;i<animations_count;i++)
|
|
{
|
|
anim = animations[i];
|
|
|
|
if ((Q_stricmp(anim->monster->targetname, targetname) == 0) ||
|
|
(Q_stricmp("all", targetname) == 0))
|
|
anim->active = active;
|
|
}
|
|
}
|
|
|
|
/*=========================================================================
|
|
Animation player command entry point.
|
|
Called in g_cmds.c
|
|
=========================================================================*/
|
|
void anim_player_cmd(edict_t *ent)
|
|
{
|
|
char *args, *arg1=NULL, *arg2=NULL;
|
|
|
|
args = gi.args();
|
|
|
|
if (!cut_up_string(&args, &arg1))
|
|
return;
|
|
|
|
cut_up_string(&args, &arg2);
|
|
|
|
// string switch
|
|
if (Q_stricmp (arg1, "capture") == 0)
|
|
anim_player_capture(arg2);
|
|
else if (Q_stricmp (arg1, "activate") == 0)
|
|
anim_player_set_active(arg2, true);
|
|
else if (Q_stricmp (arg1, "deactivate") == 0)
|
|
anim_player_set_active(arg2, false);
|
|
else if (Q_stricmp (arg1, "pause") == 0)
|
|
anim_player_pause();
|
|
else if (Q_stricmp (arg1, "still") == 0)
|
|
anim_player_still();
|
|
else if (Q_stricmp (arg1, "events") == 0)
|
|
anim_player_events();
|
|
else if (Q_stricmp (arg1, "s_next") == 0)
|
|
anim_player_advance_sequence(1);
|
|
else if (Q_stricmp (arg1, "s_prior") == 0)
|
|
anim_player_advance_sequence(-1);
|
|
else if (Q_stricmp (arg1, "s_reset") == 0)
|
|
anim_player_advance_sequence(0);
|
|
else if (Q_stricmp (arg1, "f_next") == 0)
|
|
anim_player_advance_frame(1);
|
|
else if (Q_stricmp (arg1, "f_prior") == 0)
|
|
anim_player_advance_frame(-1);
|
|
else if (Q_stricmp (arg1, "face_client") == 0)
|
|
anim_player_set_facing(DIR_AT_CLIENT);
|
|
else if (Q_stricmp (arg1, "face_para") == 0)
|
|
anim_player_set_facing(DIR_PARA_CLIENT);
|
|
else if (Q_stricmp (arg1, "face_fixed") == 0)
|
|
anim_player_set_facing(DIR_FIXED);
|
|
else if (Q_stricmp (arg1, "aim_client") == 0)
|
|
anim_player_set_aim(DIR_AT_CLIENT);
|
|
else if (Q_stricmp (arg1, "aim_para") == 0)
|
|
anim_player_set_aim(DIR_PARA_CLIENT);
|
|
else if (Q_stricmp (arg1, "aim_fixed") == 0)
|
|
anim_player_set_aim(DIR_FIXED);
|
|
else
|
|
gi.dprintf("unknown anim command <%s>\n", arg1);
|
|
|
|
//clean up
|
|
gi.TagFree(arg1);
|
|
if (arg2)
|
|
gi.TagFree(arg2);
|
|
}
|
|
|
|
#endif
|