1261 lines
36 KiB
Plaintext
1261 lines
36 KiB
Plaintext
//This file provides support for Q3-compatible player models.
|
|
|
|
/*
|
|
This module provides:
|
|
bool(string newmodelskin) Anim_SetModel
|
|
This function applies the named model (eg: "sarge" or "sarge/red") to self.
|
|
Returns false on failure. If it fails, the entity will be untouched.
|
|
void() Anim_Draw
|
|
This function adds self's segmented models to the scene.
|
|
This is intended to be called from inside a predraw function.
|
|
void() Anim_UnsetModel
|
|
This frees everything assosiated with self, but Does not flush the animation file.
|
|
entity() Anim_DupModel
|
|
Duplicates the animation state/model from self entity to a newly spawned entity.
|
|
This is expected to be used for bodyqueues.
|
|
float(string fname) Anim_ReadAnimationFile
|
|
An explicit call is not required, except as perhaps a precache.
|
|
fname is eg: "sarge", but NOT "sarge/red".
|
|
Returns -1 on failure.
|
|
Fails if list is full or animation.cfg is not found. Does not fail if a model is not found.
|
|
float() Anim_GetGender
|
|
Returns which gender the 'self' player model is.
|
|
float(string skinname) Anim_GetHeadModelIndex
|
|
Returns a modelindex assosiated with the given model/skin name.
|
|
Intended for the hud.
|
|
float(string skinname) Anim_GetHeadSkinNumber
|
|
Returns a skin number for use with the modelindex returned by Anim_GetHeadModelIndex to give the correct model/skin for the given model/skin parameter.
|
|
Intended for the hud.
|
|
vector(string skinname) Anim_GetHeadOffset
|
|
Retrieves the q3 head offset for displaying the head in a nice neat box on the hud.
|
|
|
|
if only I were allowed to use pointers... these three head functions would be a single more efficient function
|
|
|
|
*/
|
|
|
|
#ifdef MD3PMODELS
|
|
|
|
//these are the animation sequence names used in quake3.
|
|
enum {
|
|
BOTH_DEATH1,
|
|
BOTH_DEAD1,
|
|
BOTH_DEATH2,
|
|
BOTH_DEAD2,
|
|
BOTH_DEATH3,
|
|
BOTH_DEAD3,
|
|
|
|
TORSO_GESTURE,
|
|
|
|
TORSO_ATTACK,
|
|
TORSO_ATTACK2,
|
|
|
|
TORSO_DROP,
|
|
TORSO_RAISE,
|
|
|
|
TORSO_STAND,
|
|
TORSO_STAND2,
|
|
|
|
LEGS_WALKCR,
|
|
LEGS_WALK,
|
|
LEGS_RUN,
|
|
LEGS_BACK,
|
|
LEGS_SWIM,
|
|
|
|
LEGS_JUMP,
|
|
LEGS_LAND,
|
|
|
|
LEGS_JUMPB,
|
|
LEGS_LANDB,
|
|
|
|
LEGS_IDLE,
|
|
LEGS_IDLECR,
|
|
LEGS_TURN,
|
|
|
|
NUMANIMS
|
|
};
|
|
|
|
|
|
//we use qc arrays to store all the information. this results in a lot of prog space used.
|
|
#define MAXMODELS 50 //if progs size becomes an issue, reduce this.
|
|
|
|
//a size optimisation.
|
|
#define anim_firstframe A
|
|
#define anim_numframes B
|
|
#define anim_loopingframes C
|
|
#define anum_framespersecond D
|
|
|
|
//a q3 animation.cfg file contains 4 items per sequence.
|
|
nosave float anim_firstframe[NUMANIMS*MAXMODELS]; //first frame in the sequence
|
|
nosave float anim_numframes[NUMANIMS*MAXMODELS]; //number of frames in this sequence.
|
|
nosave float anim_loopingframes[NUMANIMS*MAXMODELS]; //number of frames to play while looping (jumps to total-looping)
|
|
nosave float anum_framespersecond[NUMANIMS*MAXMODELS];//number of frames to play per second.
|
|
|
|
nosave float anim_gender[MAXMODELS]; //one of the SEX_ enum values.
|
|
nosave string anim_name[MAXMODELS]; //names the (skinless) player model, so they can be cached and shared.
|
|
nosave float anim_headmodel[MAXMODELS];
|
|
nosave vector anim_headoffset[MAXMODELS];
|
|
|
|
//note: q3 assumes male.
|
|
//we assume male too, due to the player model that is typically used if we have no q3 models.
|
|
#define GENDER_DEFAULT GENDER_MALE
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//This block of code is to replace builtins which may be missing in your engine of choice.
|
|
//Its all generic maths/tag stuff, so consider it just noise :)
|
|
//this doesn't affect how the model is built.
|
|
|
|
#ifdef WORKINDP
|
|
vector getlerpedtaginfo(entity ent, float tagnum)
|
|
{
|
|
//personally I consider it a bug that this function is needed.
|
|
//this function exactly duplicates the gettaginfo builtin
|
|
//but ensures that interpolation based on frame2 happens.
|
|
//this matches how the renderer will lerp frames.
|
|
|
|
//at the time of writing, this is needed for DP to be smooth.
|
|
//fte does not support gettaginfo at all (I've been lazy due to the heirachical nature).
|
|
//this is called by rotatevectorsbytag_GTI, so it not required in fte as FTE has a working rotatevectorsbytag instead.
|
|
|
|
float frame2ness = ent.lerpfrac;
|
|
float frame1ness = 1-frame2ness;
|
|
float f1=ent.frame;
|
|
float f2=ent.frame2;
|
|
//it doesn't matter what the ent's lerpfrac currently is.
|
|
vector f1o, f1f, f1r, f1u;
|
|
vector f2o, f2f, f2r, f2u;
|
|
vector v_origin;
|
|
|
|
//make sure both frames are set, in case the builtin is fixed
|
|
ent.frame = f1;
|
|
ent.frame2 = f1;
|
|
f1o = gettaginfo(ent, tagnum);
|
|
f1r=v_right;f1f=v_forward;f1u=v_up;
|
|
|
|
ent.frame = f2;
|
|
ent.frame2 = f2;
|
|
v_origin = gettaginfo(ent, tagnum);
|
|
|
|
//restore the entity
|
|
ent.frame = f1;
|
|
ent.frame2 = f2;
|
|
|
|
//lerp the current frame2 with the cached frame1 values
|
|
v_forward = f1f*frame1ness+v_forward*frame2ness;
|
|
v_right = f1r*frame1ness+v_right*frame2ness;
|
|
v_up = f1u*frame1ness+v_up*frame2ness;
|
|
v_origin = f1o*frame1ness+v_origin*frame2ness;
|
|
|
|
return v_origin;
|
|
}
|
|
|
|
#define rotatevectorsbytag rotatevectorsbytag_GTI
|
|
vector rotatevectorsbytag_GTI(entity ent, float tagnum)
|
|
{
|
|
vector saveang=ent.angles;
|
|
vector saveorg=ent.origin;
|
|
|
|
vector oldx=v_forward, oldy=('0 0 0'-v_right), oldz=v_up;
|
|
vector oldo=ent.origin;
|
|
|
|
ent.angles = '0 0 0';
|
|
ent.origin = '0 0 0';
|
|
vector ango=getlerpedtaginfo(ent, tagnum);
|
|
ent.angles = saveang;
|
|
ent.origin = saveorg;
|
|
//note: v_right is actually left, in tags.
|
|
vector angx=v_forward, angy=v_right, angz=v_up;
|
|
|
|
angx = normalize(angx);
|
|
angy = normalize(angy);
|
|
angz = normalize(angz);
|
|
|
|
//multiply out the tag matrix with the previous matrix.
|
|
v_forward_x = angx_x*oldx_x + angx_y*oldy_x + angx_z*oldz_x;
|
|
v_forward_y = angx_x*oldx_y + angx_y*oldy_y + angx_z*oldz_y;
|
|
v_forward_z = angx_x*oldx_z + angx_y*oldy_z + angx_z*oldz_z;
|
|
v_right_x = angy_x*oldx_x + angy_y*oldy_x + angy_z*oldz_x;
|
|
v_right_y = angy_x*oldx_y + angy_y*oldy_y + angy_z*oldz_y;
|
|
v_right_z = angy_x*oldx_z + angy_y*oldy_z + angy_z*oldz_z;
|
|
v_up_x = angz_x*oldx_x + angz_y*oldy_x + angz_z*oldz_x;
|
|
v_up_y = angz_x*oldx_y + angz_y*oldy_y + angz_z*oldz_y;
|
|
v_up_z = angz_x*oldx_z + angz_y*oldy_z + angz_z*oldz_z;
|
|
|
|
v_right = '0 0 0'-v_right;
|
|
|
|
//transform the tag's origin
|
|
saveorg = oldo;
|
|
saveorg_x += ango_x * oldx_x;
|
|
saveorg_y += ango_x * oldx_y;
|
|
saveorg_z += ango_x * oldx_z;
|
|
saveorg_x += ango_y * oldy_x;
|
|
saveorg_y += ango_y * oldy_y;
|
|
saveorg_z += ango_y * oldy_z;
|
|
saveorg_x += ango_z * oldz_x;
|
|
saveorg_y += ango_z * oldz_y;
|
|
saveorg_z += ango_z * oldz_z;
|
|
return saveorg;
|
|
}
|
|
#define rotatevectorsbyangle rotatevectorsbyangle_QC
|
|
void rotatevectorsbyangle_QC(vector angle)
|
|
{
|
|
vector oldx=v_forward, oldy='0 0 0'-v_right, oldz=v_up;
|
|
angle_x = -angle_x;
|
|
makevectors(angle);
|
|
vector angx=v_forward, angy='0 0 0'-v_right, angz=v_up;
|
|
|
|
v_forward_x = angx_x*oldx_x + angx_y*oldy_x + angx_z*oldz_x;
|
|
v_forward_y = angx_x*oldx_y + angx_y*oldy_y + angx_z*oldz_y;
|
|
v_forward_z = angx_x*oldx_z + angx_y*oldy_z + angx_z*oldz_z;
|
|
v_right_x = angy_x*oldx_x + angy_y*oldy_x + angy_z*oldz_x;
|
|
v_right_y = angy_x*oldx_y + angy_y*oldy_y + angy_z*oldz_y;
|
|
v_right_z = angy_x*oldx_z + angy_y*oldy_z + angy_z*oldz_z;
|
|
v_up_x = angz_x*oldx_x + angz_y*oldy_x + angz_z*oldz_x;
|
|
v_up_y = angz_x*oldx_y + angz_y*oldy_y + angz_z*oldz_y;
|
|
v_up_z = angz_x*oldx_z + angz_y*oldy_z + angz_z*oldz_z;
|
|
|
|
v_right = '0 0 0'-v_right;
|
|
}
|
|
#define RotateVectorsByVectors RotateVectorsByVectorsDP
|
|
|
|
//
|
|
#define skinforname(i,n) 0
|
|
#endif
|
|
|
|
//end noisy maths.
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//a utility function required by sexedsound so we don't try playing missing sounds (its common for male models to be missing some)
|
|
float(string str) fileexists =
|
|
{
|
|
//we use searches because its more likly to be cached than reading a 200kb file from the disk every time.
|
|
local float search;
|
|
search = search_begin(str, false, true);
|
|
if (search < 0)
|
|
{
|
|
return false;
|
|
}
|
|
search_end(search);
|
|
return true;
|
|
};
|
|
|
|
//plays a CHAN_VOICE sound around the given entity.
|
|
void(entity ent, string soundname) sexedsound =
|
|
{
|
|
string str, full;
|
|
if (self == player_local)
|
|
if (CVARF(cg_noselfsounds))
|
|
return; //option to disable sounds from local player
|
|
|
|
str = strcat("player/", anim_name[self.modelnum], "/", soundname);
|
|
full = strcat("sound/", str);
|
|
if (fileexists(full))
|
|
{
|
|
precache_sound(str);
|
|
sound(self, CHAN_VOICE, str, 1, 1, 95 + random()*10);
|
|
}
|
|
else
|
|
{
|
|
str = strcat("player/sarge/", soundname); // :(
|
|
precache_sound(str);
|
|
sound(self, CHAN_VOICE, str, 1, 1, 95 + random()*10);
|
|
}
|
|
};
|
|
|
|
|
|
//our player's frames changed to some new sequence.
|
|
//our animation function will animate acordingly.
|
|
|
|
static void(float anum) ForceToAnim =
|
|
{
|
|
if (anum <= TORSO_STAND2)
|
|
{
|
|
self.torsoent.animnum = anum;
|
|
self.torsoent.framechangetime = time;
|
|
}
|
|
if (anum <= BOTH_DEAD3 || anum >= LEGS_WALKCR)
|
|
{
|
|
self.legsent.animnum = anum;
|
|
self.legsent.framechangetime = time;
|
|
}
|
|
};
|
|
|
|
//this function is per-segment.
|
|
//it updates the frame/frame2/lerpfrac values and loops/caps the animation.
|
|
//returns true if the end of the current animation was reached.
|
|
float(entity ent) animate =
|
|
{
|
|
//true if looped
|
|
float anum;
|
|
float ft;
|
|
float fnum;
|
|
float numframes;
|
|
float fps;
|
|
float loopingframes;
|
|
float ret;
|
|
|
|
anum = (ent.modelnum * NUMANIMS) + ent.animnum;
|
|
|
|
ft = time - ent.framechangetime;
|
|
numframes = anim_numframes[anum];
|
|
fps = anum_framespersecond[anum];
|
|
|
|
ft *= fps;
|
|
fnum = floor(ft);
|
|
ft = ft - fnum; //time into the frame.
|
|
ret = fnum >= numframes;
|
|
if (ret)
|
|
{
|
|
loopingframes = anim_loopingframes[anum];
|
|
|
|
if (loopingframes)
|
|
{
|
|
fnum -= numframes - loopingframes;
|
|
fnum = fnum - floor(fnum/loopingframes)*loopingframes;
|
|
fnum += numframes - loopingframes;
|
|
}
|
|
else
|
|
{ //stop at the end of it.
|
|
fnum = numframes-1;
|
|
ft = 1;
|
|
}
|
|
|
|
if (numframes == 1)
|
|
ent.frame2 = ent.frame;
|
|
}
|
|
fnum += anim_firstframe[anum];
|
|
|
|
if (ent.frame2 == -1)
|
|
{ //special flag to make it snap for the initial frame
|
|
ent.frame = fnum;
|
|
ent.frame2 = fnum;
|
|
}
|
|
else if (ent.frame != fnum)
|
|
{
|
|
ent.frame2 = ent.frame;
|
|
ent.frame = fnum;
|
|
}
|
|
ent.lerpfrac = 1-ft;
|
|
if (ent.lerpfrac<=0)
|
|
ent.frame2 = ent.frame;
|
|
|
|
return ret;
|
|
};
|
|
|
|
.float wasinair;
|
|
//this function checks to see if the player is jumping/flying/dead/moving, and just updates the legs to match the current events and time.
|
|
void() LegsUpdateAnim =
|
|
{
|
|
float inair;
|
|
|
|
tracebox(self.origin, self.mins, self.maxs, self.origin-'0 0 2', FALSE, self);
|
|
|
|
if (trace_fraction == 1)
|
|
inair = self.velocity_z;
|
|
else
|
|
inair = 0;
|
|
|
|
if (self.legsent.animnum > BOTH_DEAD3)
|
|
if ((self.wasinair!=0) != (inair!=0))
|
|
{
|
|
if (trace_fraction == 1) //in air
|
|
{
|
|
if (self.velocity_z > 5)
|
|
{
|
|
makevectors(self.angles);
|
|
if (v_forward * self.velocity >= 0)
|
|
ForceToAnim(LEGS_JUMP);
|
|
else
|
|
ForceToAnim(LEGS_JUMPB);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (inair < -5 || self.wasinair < -5)
|
|
{
|
|
if (self.animnum == LEGS_JUMPB)
|
|
ForceToAnim(LEGS_LANDB);
|
|
else
|
|
ForceToAnim(LEGS_LAND);
|
|
}
|
|
}
|
|
self.wasinair = inair;
|
|
}
|
|
|
|
if (vlen(self.velocity) > 0.1)
|
|
{
|
|
if (self.legsent.animnum == LEGS_IDLE)
|
|
{
|
|
if (self.velocity * v_forward > 0)
|
|
ForceToAnim(LEGS_RUN);
|
|
else
|
|
ForceToAnim(LEGS_BACK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (self.legsent.animnum == LEGS_RUN || self.legsent.animnum == LEGS_BACK)
|
|
ForceToAnim(LEGS_IDLE);
|
|
}
|
|
|
|
if (animate(self.legsent))
|
|
{
|
|
if (self.legsent.animnum <= BOTH_DEAD3)
|
|
{
|
|
return; //don't play the idle anim when dead.
|
|
}
|
|
if (self.wasinair)
|
|
{
|
|
return; //don't change the legs anim whilst flying.
|
|
}
|
|
|
|
if (self.velocity_x || self.velocity_y)
|
|
{
|
|
if (self.velocity * v_forward > 0)
|
|
ForceToAnim(LEGS_RUN);
|
|
else
|
|
ForceToAnim(LEGS_BACK);
|
|
}
|
|
else
|
|
{
|
|
ForceToAnim(LEGS_IDLE);
|
|
}
|
|
|
|
animate(self.legsent);
|
|
}
|
|
};
|
|
|
|
//updates the torso's animations. basically just shoots/gestures/or otherwise just updates to the current time.
|
|
void() TorsoUpdateAnim =
|
|
{
|
|
if (animate(self.torsoent))
|
|
{
|
|
playerframe f = self.frame;
|
|
if (f >= playerframe::axdeth1 && f <= playerframe::deathe9)
|
|
return; //dead.
|
|
|
|
if (f == playerframe::nailatt1 || f == playerframe::nailatt2 ||
|
|
f == playerframe::light1 || f == playerframe::light2)
|
|
ForceToAnim(TORSO_ATTACK); //these ones loop
|
|
else if (random() < 0.005 && !(self.velocity_x || self.velocity_y) && (chasecam || self != player_local))
|
|
{ //randomly taunt, when standing still, when not first-person (making the sounds is just confusing when first person)
|
|
sexedsound(self, "taunt.wav");
|
|
ForceToAnim(TORSO_GESTURE);
|
|
}
|
|
else if ((f >= playerframe::axrun1 && f <= playerframe::axrun6) ||
|
|
(f >= playerframe::axstnd1 && f <= playerframe::axstnd12) ||
|
|
(f >= playerframe::axpain1 && f <= playerframe::axpain6))
|
|
ForceToAnim(TORSO_STAND2);
|
|
else
|
|
ForceToAnim(TORSO_STAND);
|
|
animate(self.legsent);
|
|
}
|
|
};
|
|
|
|
//adds a q3-shell around the model based on which powerups they have active.
|
|
//and just basically acts as a wrapper to addentity.
|
|
void(entity ent) AddModelWithEffects =
|
|
{
|
|
if (self.sveffects & SVE_INVIS)
|
|
ent.forceshader = shaderforname("powerups/invisibility");
|
|
|
|
addentity(ent);
|
|
if (self.sveffects & SVE_QUAD)
|
|
{
|
|
ent.fatness = -2;
|
|
ent.forceshader = shaderforname("powerups/quad");
|
|
addentity(ent);
|
|
}
|
|
if (self.sveffects & SVE_GOD)
|
|
{
|
|
ent.fatness = -2;
|
|
ent.forceshader = shaderforname("powerups/regen");
|
|
addentity(ent);
|
|
}
|
|
ent.fatness = 0;
|
|
ent.forceshader = 0;
|
|
};
|
|
|
|
//this function is called inside your base entity's predraw function (which should then return false)
|
|
nonstatic void() Anim_Draw =
|
|
{
|
|
vector tf, tr, tu;
|
|
vector ang;
|
|
float move;
|
|
string weaponname = 0;
|
|
|
|
self.modelindex = 0; //should never have been anything else.
|
|
|
|
|
|
if (player_local == self && !chasecam)
|
|
{
|
|
//we still add the local player entity, although only to mirrors.
|
|
self.legsent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;
|
|
self.torsoent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;
|
|
self.headent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;
|
|
self.weaponent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;
|
|
}
|
|
else
|
|
{
|
|
self.legsent.renderflags = RF_USEAXIS;
|
|
self.torsoent.renderflags = RF_USEAXIS;
|
|
self.headent.renderflags = RF_USEAXIS;
|
|
self.weaponent.renderflags = RF_USEAXIS;
|
|
}
|
|
|
|
if (!(self.frame >= playerframe::axdeth1 && self.frame <= playerframe::deathe9))
|
|
{
|
|
//if they're still alive
|
|
//animate the legs a bit so they turn to the player, but not for 1-degree turns. little more realism there.
|
|
|
|
ang_y = self.angles_y;
|
|
if (self.velocity_x||self.velocity_y)
|
|
{
|
|
makevectors(ang);
|
|
tf = self.velocity;
|
|
tf_z = 0;
|
|
tf = normalize(tf);
|
|
if (v_forward*tf > -0.01)
|
|
self.legsent.ideal_yaw = vectoyaw(tf);
|
|
else
|
|
self.legsent.ideal_yaw = vectoyaw(tf)+180;
|
|
}
|
|
else
|
|
{
|
|
move = ang_y - self.legsent.ideal_yaw;
|
|
if (move < -180)
|
|
move += 360;
|
|
if (move > 180)
|
|
move -= 360;
|
|
|
|
if (move*move > 30*30)
|
|
ForceToAnim(LEGS_TURN);
|
|
|
|
if (self.legsent.animnum == LEGS_TURN || self.legsent.animnum == LEGS_JUMP)
|
|
self.legsent.ideal_yaw = ang_y;
|
|
}
|
|
other = self;
|
|
self = self.legsent;
|
|
self.yaw_speed = 5;//180*frametime;
|
|
|
|
//clamp the legs to never be more than 90 degrees
|
|
move = other.angles_y-self.angles_y;
|
|
if (move < -180)
|
|
move += 360;
|
|
if (move > 180)
|
|
move -= 360;
|
|
//turn the legs
|
|
if (move*move > 95*95) //snap
|
|
{
|
|
if (move>0)
|
|
move=20;
|
|
else
|
|
move=-20;
|
|
self.angles_y = other.angles_y - move;
|
|
self = other;
|
|
}
|
|
else
|
|
ChangeYaw();
|
|
self = other;
|
|
}
|
|
|
|
|
|
if (self.frame != self.frame2)
|
|
{
|
|
//see if the player changed to any animations.
|
|
if (self.frame >= playerframe::axdeth1 && self.frame <= playerframe::axdeth8 && self.legsent.animnum != BOTH_DEATH1)
|
|
{
|
|
sexedsound(self, "death1.wav");
|
|
ForceToAnim(BOTH_DEATH1);
|
|
}
|
|
else if (self.frame == playerframe::axdeth9 && self.frame2 != playerframe::axdeth8)
|
|
ForceToAnim(BOTH_DEAD1); //suddenly appeared with this frame (otherwise we just flow into it)
|
|
|
|
else if (self.frame == playerframe::deatha1 && self.frame <= playerframe::deatha10 && self.legsent.animnum != BOTH_DEATH2)
|
|
{
|
|
sexedsound(self, "death2.wav");
|
|
ForceToAnim(BOTH_DEATH2);
|
|
}
|
|
else if (self.frame == playerframe::deatha11 && self.frame2 != playerframe::deatha10)
|
|
ForceToAnim(BOTH_DEAD2); //suddenly appeared with this frame (otherwise we just flow into it)
|
|
|
|
else if (self.frame == playerframe::deathb1 && self.frame <= playerframe::deathb8 && self.legsent.animnum != BOTH_DEATH3)
|
|
{
|
|
sexedsound(self, "death3.wav");
|
|
ForceToAnim(BOTH_DEATH3);
|
|
}
|
|
else if (self.frame == playerframe::deathb9 && self.frame2 != playerframe::deathb8)
|
|
ForceToAnim(BOTH_DEAD3); //suddenly appeared with this frame (otherwise we just flow into it)
|
|
|
|
else if (self.frame == playerframe::deathc1 && self.frame <= playerframe::deathc14 && self.legsent.animnum != BOTH_DEATH1)
|
|
{
|
|
sexedsound(self, "death1.wav");
|
|
ForceToAnim(BOTH_DEATH1);
|
|
}
|
|
else if (self.frame == playerframe::deathc15 && self.frame2 != playerframe::deathc14)
|
|
ForceToAnim(BOTH_DEAD1); //suddenly appeared with this frame (otherwise we just flow into it)
|
|
|
|
else if (self.frame == playerframe::deathd1 && self.frame <= playerframe::deathd8 && self.legsent.animnum != BOTH_DEATH2)
|
|
{
|
|
sexedsound(self, "death2.wav");
|
|
ForceToAnim(BOTH_DEATH2);
|
|
}
|
|
else if (self.frame == playerframe::deathd9 && self.frame2 != playerframe::deathd8)
|
|
ForceToAnim(BOTH_DEAD2); //suddenly appeared with this frame (otherwise we just flow into it)
|
|
|
|
else if (self.frame == playerframe::deathe1 && self.frame <= playerframe::deathe8 && self.legsent.animnum != BOTH_DEATH3)
|
|
{
|
|
sexedsound(self, "death3.wav");
|
|
ForceToAnim(BOTH_DEATH3);
|
|
}
|
|
else if (self.frame == playerframe::deathe9 && self.frame2 != playerframe::deathe8)
|
|
ForceToAnim(BOTH_DEAD3); //suddenly appeared with this frame (otherwise we just flow into it)
|
|
|
|
else if ((self.frame == playerframe::nailatt1 || self.frame == playerframe::nailatt2) &&
|
|
(self.frame2 != playerframe::nailatt1 && self.frame2 != playerframe::nailatt2))
|
|
ForceToAnim(TORSO_ATTACK);
|
|
else if ((self.frame == playerframe::light1 || self.frame == playerframe::light2) &&
|
|
(self.frame2 != playerframe::light1 && self.frame2 != playerframe::light2))
|
|
ForceToAnim(TORSO_ATTACK);
|
|
else if (self.frame == playerframe::rockatt1)
|
|
ForceToAnim(TORSO_ATTACK);
|
|
else if (self.frame == playerframe::shotatt1)
|
|
ForceToAnim(TORSO_ATTACK);
|
|
else if (self.frame == playerframe::axatt1)
|
|
ForceToAnim(TORSO_ATTACK2);
|
|
else if (self.frame == playerframe::axattb1)
|
|
ForceToAnim(TORSO_ATTACK2);
|
|
else if (self.frame == playerframe::axattc1)
|
|
ForceToAnim(TORSO_ATTACK2);
|
|
else if (self.frame == playerframe::axattd1)
|
|
ForceToAnim(TORSO_ATTACK2);
|
|
else if (self.frame2 >= playerframe::axdeth1 && self.frame2 <= playerframe::deathe9 && !(self.frame >= playerframe::axdeth1 && self.frame <= playerframe::deathe9))
|
|
{ //respawned
|
|
ForceToAnim(LEGS_IDLE);
|
|
ForceToAnim(TORSO_STAND);
|
|
}
|
|
self.frame2 = self.frame;
|
|
}
|
|
|
|
//add a dynamic light around the player if they have any powerups.
|
|
if (self.sveffects & SVE_QUAD)
|
|
ang_z = 1;
|
|
if (self.sveffects & SVE_GOD)
|
|
ang_x = 1;
|
|
ang_y = 0;
|
|
if (ang != '0 0 0')
|
|
{
|
|
dynamiclight_add(self.origin, 400, ang);
|
|
}
|
|
|
|
//if they're meant to be transparent, propogate that.
|
|
self.legsent.alpha = self.torsoent.alpha = self.headent.alpha = self.weaponent.alpha = self.alpha;
|
|
//LegsUpdateAnim needs its origin early, for stff
|
|
self.legsent.origin = self.origin;
|
|
|
|
//update the animation lerps
|
|
makevectors(self.legsent.angles);
|
|
LegsUpdateAnim();
|
|
TorsoUpdateAnim();
|
|
|
|
//add the legs into the scene, with their lagged angles.
|
|
AddModelWithEffects(self.legsent);
|
|
//legs are in place, matricies are set up for them.
|
|
|
|
//work out where to put the torso by querying the tag
|
|
self.torsoent.origin = rotatevectorsbytag(self.legsent, self.torsoent.tag_index);//figure out the torso position
|
|
//torso's angles are not lagged, and must always point directly at the target, so rotate by the extra angles.
|
|
ang = self.angles;
|
|
ang_y = self.angles_y - self.legsent.angles_y; //keep the angle on the legs
|
|
ang_x*=2;
|
|
if (self.legsent.animnum > BOTH_DEAD3)
|
|
rotatevectorsbyangle(ang); //rotate the torso (when dead the whole thing acts as one model with no custom angles inside)
|
|
AddModelWithEffects(self.torsoent);
|
|
//torso is now added to the scene.
|
|
|
|
//save the direction we're looking in for the weapon which is added after the head.
|
|
tf = v_forward;
|
|
tr = v_right;
|
|
tu = v_up;
|
|
|
|
//now work out where to put the head
|
|
self.headent.origin = rotatevectorsbytag(self.torsoent, self.headent.tag_index);//
|
|
ang_y = sin(time)*22.5;
|
|
ang_x = cos(time*0.4532)*11.25;
|
|
if (self.legsent.animnum > BOTH_DEAD3)
|
|
rotatevectorsbyangle(ang); //make the head around a bit
|
|
AddModelWithEffects(self.headent);
|
|
//head is now in place
|
|
|
|
if (self.frame >= playerframe::axdeth1 && self.frame <= playerframe::deathe9)
|
|
return; //don't show the weapon in death frames.
|
|
|
|
//and revert the matrix back to how it was before the head.
|
|
v_forward = tf;
|
|
v_right = tr;
|
|
v_up = tu;
|
|
|
|
move = self.sveffects&15;
|
|
switch (move)
|
|
{
|
|
case 12: //axe
|
|
weaponname = "models/weapons2/gauntlet/gauntlet";
|
|
break;
|
|
default:
|
|
case 0: //shotgun
|
|
weaponname = "models/weapons2/railgun/railgun"; //well... the sniper's prefered weapon, at least. :p
|
|
break;
|
|
case 1: //super shotgun
|
|
weaponname = "models/weapons2/shotgun/shotgun";
|
|
break;
|
|
case 2: //nailgun
|
|
weaponname = "models/weapons2/machinegun/machinegun";
|
|
break;
|
|
case 3: //super nailgun
|
|
weaponname = "models/weapons2/plasma/plasma";
|
|
break;
|
|
case 4: //grenade launcher
|
|
weaponname = "models/weapons2/grenadel/grenadel";
|
|
break;
|
|
case 5: //rocket launcher
|
|
weaponname = "models/weapons2/rocketl/rocketl";
|
|
break;
|
|
case 6: //lightning gun
|
|
weaponname = "models/weapons2/lightning/lightning";
|
|
break;
|
|
}
|
|
|
|
setmodel(self.weaponent, strcat(weaponname, ".md3"));
|
|
|
|
//rotate by a tag on the torso
|
|
self.weaponent.origin = rotatevectorsbytag(self.torsoent, self.weaponent.tag_index);//place the weapon in the hand
|
|
//and add it.
|
|
AddModelWithEffects(self.weaponent);
|
|
|
|
//this check is a little wrong.
|
|
if (self.torsoent.animnum == TORSO_ATTACK || self.torsoent.animnum == TORSO_ATTACK2)
|
|
{
|
|
move = gettagindex(self.weaponent, "tag_flash");
|
|
if (move)
|
|
{
|
|
//they're shooting something. make a muzzleflash appear at the end of their weapon.
|
|
self.weaponent.origin = rotatevectorsbytag(self.weaponent, move);
|
|
setmodel(self.weaponent, strcat(weaponname, "_flash.md3"));
|
|
AddModelWithEffects(self.weaponent);
|
|
}
|
|
}
|
|
};
|
|
|
|
//remove our attached models, restoring the player model to being a boring player.
|
|
nonstatic void() Anim_UnsetModel =
|
|
{
|
|
if (self.torsoent)
|
|
remove(self.torsoent);
|
|
if (self.headent)
|
|
remove(self.headent);
|
|
if (self.legsent)
|
|
remove(self.legsent);
|
|
if (self.weaponent)
|
|
remove(self.weaponent);
|
|
|
|
self.torsoent = world;
|
|
self.headent = world;
|
|
self.legsent = world;
|
|
self.weaponent = world;
|
|
self.modelnum = -1;
|
|
|
|
setmodel(self, self.model);
|
|
};
|
|
|
|
nonstatic float() Anim_GetGender =
|
|
{
|
|
if (self.headent)
|
|
return anim_gender[self.headent.modelnum];
|
|
return GENDER_DEFAULT;
|
|
};
|
|
|
|
//Attempts to read the animation file for the named q3 player model
|
|
//-1 on failure.
|
|
nonstatic float(string modname) Anim_ReadAnimationFile =
|
|
{
|
|
local float modnum;
|
|
local string str;
|
|
local float file;
|
|
local float sequencenum = 0;
|
|
local float stupid;
|
|
|
|
if (modname == "")
|
|
return -1;
|
|
|
|
for (modnum = 0; modnum < MAXMODELS; modnum++)
|
|
{
|
|
if (anim_name[modnum] == modname)
|
|
return modnum; //already loaded
|
|
if (anim_name[modnum] == "")
|
|
break; //empty slot
|
|
}
|
|
if (modnum == MAXMODELS)
|
|
{ //list is full
|
|
print("Too many models\n");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
str = strcat("models/players/", modname, "/animation.cfg");
|
|
|
|
file = fopen(str, FILE_READ);
|
|
if (file < 0)
|
|
{
|
|
print(sprintf("fopen %S failed\n", str));
|
|
return -1;
|
|
}
|
|
|
|
modname= strzone(modname);
|
|
anim_name[modnum] = modname;
|
|
|
|
//precache them.
|
|
precache_model(strcat("models/players/", modname, "/upper.md3"));
|
|
precache_model(strcat("models/players/", modname, "/lower.md3"));
|
|
precache_model(strcat("models/players/", modname, "/head.md3"));
|
|
|
|
anim_headmodel[modnum] = getmodelindex(strcat("models/players/", modname, "/head.md3"));
|
|
|
|
//default general values
|
|
anim_gender[modnum] = GENDER_DEFAULT;
|
|
anim_headoffset[modnum] = '0 0 0';
|
|
|
|
for (;;)
|
|
{
|
|
str = fgets(file);
|
|
if not (str) break;
|
|
|
|
tokenize(str);
|
|
str = argv(0);
|
|
if (str == "")
|
|
continue;
|
|
else if (str == "sex")
|
|
{
|
|
str = argv(1);
|
|
if (str == "m" || str == "M")
|
|
anim_gender[modnum] = GENDER_MALE;
|
|
else if (str == "f" || str == "F")
|
|
anim_gender[modnum] = GENDER_FEMALE;
|
|
else //n or N
|
|
anim_gender[modnum] = GENDER_NEUTER;
|
|
}
|
|
else if (str == "headoffset")
|
|
{
|
|
vector v;
|
|
v_x = stof(argv(1));
|
|
v_y = stof(argv(2));
|
|
v_z = stof(argv(3));
|
|
anim_headoffset[modnum] = v;
|
|
}
|
|
else if (str == "footsteps")
|
|
{
|
|
//we don't play footsteps anyway
|
|
}
|
|
else if (str2chr(str,0) >= '0' && str2chr(str,0) <= '9')
|
|
{
|
|
if (sequencenum == NUMANIMS)
|
|
print("animation file \"" "models/players/", modname, "/animation.cfg" "\" contains too many animations\n");
|
|
else
|
|
{
|
|
if (sequencenum == LEGS_WALKCR) //for some stupid reason, the leg frames start where the torso leaves
|
|
{
|
|
stupid = stof(str);
|
|
stupid = stupid - anim_firstframe[(modnum * NUMANIMS) + TORSO_GESTURE];
|
|
}
|
|
else stupid = 0;
|
|
anim_firstframe[(modnum * NUMANIMS) + sequencenum] = stof(str) - stupid;
|
|
anim_numframes[(modnum * NUMANIMS) + sequencenum] = stof(argv(1));
|
|
anim_loopingframes[(modnum * NUMANIMS) + sequencenum] = stof(argv(2));
|
|
anum_framespersecond[(modnum * NUMANIMS) + sequencenum] = stof(argv(3));
|
|
sequencenum++;
|
|
}
|
|
}
|
|
else
|
|
print("animation.cfg contains unrecognised token ", str, "\n");
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
if (sequencenum != NUMANIMS)
|
|
print("animation.cfg is incompleate\n");
|
|
}
|
|
return modnum;
|
|
}
|
|
|
|
//attempts to apply a player model/skin to the given entity.
|
|
//this may load the configuration.cfg
|
|
//skinname is of the form: ranger/default
|
|
nonstatic float(string skinname) Anim_SetModel =
|
|
{
|
|
local string lowermodelname;
|
|
local string uppermodelname;
|
|
local string headmodelname;
|
|
|
|
local string lowerskinname;
|
|
local string upperskinname;
|
|
local string headskinname;
|
|
|
|
local float lowermodnum;
|
|
local float uppermodnum;
|
|
local float headmodnum;
|
|
|
|
local float slashpos;
|
|
|
|
tokenize(skinname);
|
|
lowermodelname = argv(2);
|
|
uppermodelname = argv(1);
|
|
headmodelname = argv(0);
|
|
|
|
slashpos = strstrofs(lowermodelname, "/");
|
|
lowerskinname = substring(lowermodelname, slashpos+1, -1);
|
|
lowermodelname = substring(lowermodelname, 0, slashpos);
|
|
|
|
slashpos = strstrofs(uppermodelname, "/");
|
|
upperskinname = substring(uppermodelname, slashpos+1, -1);
|
|
uppermodelname = substring(uppermodelname, 0, slashpos);
|
|
|
|
slashpos = strstrofs(headmodelname, "/");
|
|
headskinname = substring(headmodelname, slashpos+1, -1);
|
|
headmodelname = substring(headmodelname, 0, slashpos);
|
|
|
|
//seeing as we support loading each part from a different player model (well, q3 does)
|
|
//we load it three times.
|
|
lowermodnum = Anim_ReadAnimationFile(lowermodelname);
|
|
uppermodnum = Anim_ReadAnimationFile(uppermodelname);
|
|
headmodnum = Anim_ReadAnimationFile(headmodelname);
|
|
|
|
//all failed
|
|
if (lowermodnum < 0 && uppermodnum < 0 && headmodnum < 0)
|
|
return false; //failed to load the animation.
|
|
|
|
//make sure that all three parts are valid or something.
|
|
if (headmodnum < 0)
|
|
{
|
|
if (lowermodnum < 0)
|
|
{
|
|
headmodnum = uppermodnum;
|
|
headmodelname = uppermodelname;
|
|
headskinname = upperskinname;
|
|
}
|
|
else
|
|
{
|
|
headmodnum = lowermodnum;
|
|
headmodelname = lowermodelname;
|
|
headskinname = lowerskinname;
|
|
}
|
|
}
|
|
if (lowermodnum < 0)
|
|
{
|
|
lowermodnum = headmodnum;
|
|
lowermodelname = headmodelname;
|
|
lowerskinname = headskinname;
|
|
}
|
|
if (uppermodnum < 0)
|
|
{
|
|
uppermodnum = headmodnum;
|
|
uppermodelname = headmodelname;
|
|
upperskinname = headskinname;
|
|
}
|
|
|
|
//spawn the attaching ents
|
|
if (!self.torsoent)
|
|
self.torsoent = spawn();
|
|
if (!self.headent)
|
|
self.headent = spawn();
|
|
if (!self.legsent)
|
|
self.legsent = spawn();
|
|
if (!self.weaponent)
|
|
self.weaponent = spawn();
|
|
|
|
//give them the correct model
|
|
setmodel(self.legsent, strcat("models/players/", anim_name[lowermodnum], "/lower.md3"));
|
|
setmodel(self.torsoent, strcat("models/players/", anim_name[uppermodnum], "/upper.md3"));
|
|
setmodel(self.headent, strcat("models/players/", anim_name[headmodnum], "/head.md3"));
|
|
|
|
// precache_model(AXEMODELNAME);
|
|
// precache_model(WEAPONMODELNAME);
|
|
|
|
|
|
self.legsent.owner = self;
|
|
self.headent.owner = self;
|
|
self.torsoent.owner = self;
|
|
self.weaponent.owner = self;
|
|
|
|
self.drawmask = MASK_NORMAL; //general view.
|
|
|
|
//find the tags so we don't do it every single frame
|
|
self.torsoent.tag_index = gettagindex(self.legsent, "tag_torso");
|
|
self.headent.tag_index = gettagindex(self.torsoent, "tag_head");
|
|
self.weaponent.tag_index = gettagindex(self.torsoent, "tag_weapon");
|
|
|
|
self.modelnum = headmodnum;
|
|
self.legsent.modelnum = lowermodnum;
|
|
self.headent.modelnum = headmodnum;
|
|
self.torsoent.modelnum = uppermodnum;
|
|
self.weaponent.modelnum = lowermodnum;
|
|
|
|
self.legsent.colormap = self.colormap;
|
|
self.headent.colormap = self.colormap;
|
|
self.torsoent.colormap = self.colormap;
|
|
self.weaponent.colormap = self.colormap;
|
|
|
|
if (!lowerskinname)
|
|
lowerskinname = "default";
|
|
if (!upperskinname)
|
|
upperskinname = "default";
|
|
if (!headskinname)
|
|
headskinname = "default";
|
|
|
|
//find which skin number to use for the given skin file
|
|
if (stof(lowerskinname))
|
|
self.legsent.skin = stof(lowerskinname);
|
|
else
|
|
self.legsent.skin = skinforname(self.legsent.modelindex, strcat("models/players/", anim_name[lowermodnum], "/lower_", lowerskinname, ".skin"));
|
|
if (stof(upperskinname))
|
|
self.torsoent.skin = stof(upperskinname);
|
|
else
|
|
self.torsoent.skin = skinforname(self.torsoent.modelindex, strcat("models/players/", anim_name[uppermodnum], "/upper_", upperskinname, ".skin"));
|
|
if (stof(headskinname))
|
|
self.headent.skin = stof(headskinname);
|
|
else
|
|
self.headent.skin = skinforname(self.headent.modelindex, strcat("models/players/", anim_name[headmodnum], "/head_", headskinname, ".skin"));
|
|
|
|
//setup the initial sequences.
|
|
ForceToAnim(LEGS_IDLE);
|
|
ForceToAnim(TORSO_STAND);
|
|
|
|
//so it takes effect fully and immediatly... well, the first time its drawn, anyway.
|
|
self.legsent.framechangetime = -50;
|
|
self.torsoent.framechangetime = -50;
|
|
self.legsent.frame2 = -1;
|
|
self.torsoent.frame2 = -1;
|
|
|
|
self.frame2 = -1;
|
|
self.lerpfrac = 0;
|
|
|
|
return true;
|
|
};
|
|
|
|
entity(entity src) CloneModel =
|
|
{
|
|
local entity dest;
|
|
dest = spawn();
|
|
|
|
dest.modelnum = src.modelnum;
|
|
dest.animnum = src.animnum;
|
|
dest.colormap = src.colormap;
|
|
dest.tag_index = src.tag_index;
|
|
dest.skin = src.skin;
|
|
dest.predraw = src.predraw;
|
|
dest.frame = src.frame;
|
|
dest.frame2 = src.frame2;
|
|
dest.model = src.model;
|
|
dest.modelindex = src.modelindex;
|
|
dest.drawmask = src.drawmask;
|
|
dest.origin = src.origin;
|
|
dest.angles = src.angles;
|
|
dest.mins = src.mins;
|
|
dest.maxs = src.maxs;
|
|
|
|
return dest;
|
|
};
|
|
|
|
nonstatic entity() Anim_DupModel =
|
|
{
|
|
local entity o, n;
|
|
o = self;
|
|
n = self = CloneModel(o);
|
|
|
|
self.legsent = CloneModel(o.legsent);
|
|
self.headent = CloneModel(o.headent);
|
|
self.torsoent = CloneModel(o.torsoent);
|
|
self.weaponent = CloneModel(o.weaponent);
|
|
self.velocity = o.velocity;
|
|
|
|
|
|
self = o;
|
|
return n;
|
|
};
|
|
|
|
nonstatic float(string skinname) Anim_GetHeadModelIndex =
|
|
{
|
|
float slashpos;
|
|
string modelname;
|
|
float modnum;
|
|
|
|
tokenize(skinname);
|
|
modelname = argv(0);
|
|
if (modelname == "")
|
|
return 0; //an invalid modelindex.
|
|
|
|
slashpos = strstrofs(modelname, "/");
|
|
modelname = substring(modelname, 0, slashpos);
|
|
|
|
//seeing as we support loading each part from a different player model (well, q3 does)
|
|
//we load it three times.
|
|
modnum = Anim_ReadAnimationFile(modelname);
|
|
if (modnum < 0)
|
|
return 0;
|
|
|
|
return anim_headmodel[modnum];
|
|
};
|
|
|
|
nonstatic float(string skinname) Anim_GetHeadSkinNumber =
|
|
{
|
|
float slashpos;
|
|
string modelname;
|
|
float modnum;
|
|
|
|
tokenize(skinname);
|
|
modelname = argv(0);
|
|
if (modelname == "")
|
|
return 0; //0 = default
|
|
|
|
slashpos = strstrofs(modelname, "/");
|
|
skinname = substring(modelname, slashpos+1, -1);
|
|
modelname = substring(modelname, 0, slashpos);
|
|
|
|
if (stof(skinname))
|
|
return stof(skinname);
|
|
|
|
//seeing as we support loading each part from a different player model (well, q3 does)
|
|
//we load it three times.
|
|
modnum = Anim_ReadAnimationFile(modelname);
|
|
if (modnum < 0)
|
|
return 0;
|
|
|
|
if (!skinname)
|
|
skinname = "default";
|
|
|
|
return skinforname(anim_headmodel[modnum], strcat("models/players/", modelname, "/head_", skinname, ".skin"));
|
|
};
|
|
|
|
nonstatic vector(string skinname) Anim_GetHeadOffset =
|
|
{
|
|
float slashpos;
|
|
string modelname;
|
|
float modnum;
|
|
|
|
tokenize(skinname);
|
|
modelname = argv(0);
|
|
if (modelname == "")
|
|
return '0 0 0'; //an invalid modelindex.
|
|
|
|
slashpos = strstrofs(modelname, "/");
|
|
modelname = substring(modelname, 0, slashpos);
|
|
|
|
//seeing as we support loading each part from a different player model (well, q3 does)
|
|
//we load it three times.
|
|
modnum = Anim_ReadAnimationFile(modelname);
|
|
if (modnum < 0)
|
|
return '0 0 0';
|
|
return anim_headoffset[modnum];
|
|
}
|
|
|
|
|
|
|
|
static float(float channel, string soundname, vector pos, float vol, float attenuation, float flags) ServerSoundStartRequest =
|
|
{ //the server started a sound on an entity that the csqc has control over.
|
|
if (!self.headent)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (soundname == "player/plyrjmp8.wav")
|
|
{
|
|
if (self == player_local)
|
|
if (CVARF(cg_noselfjumpsound))
|
|
return true; //option to disable jump noise for local player.
|
|
sexedsound(self, "jump1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/gasp1.wav")
|
|
{
|
|
sexedsound(self, "gasp.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/gasp2.wav")
|
|
{
|
|
sexedsound(self, "gasp.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/land.wav")
|
|
{
|
|
sexedsound(self, "fall1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/land2.wav")
|
|
{
|
|
sexedsound(self, "fall1.wav");
|
|
return true;
|
|
}
|
|
|
|
if (soundname == "player/pain1.wav")
|
|
{
|
|
sexedsound(self, "pain25_1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/pain2.wav")
|
|
{
|
|
sexedsound(self, "pain50_1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/pain3.wav")
|
|
{
|
|
sexedsound(self, "pain75_1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/pain4.wav")
|
|
{
|
|
sexedsound(self, "pain100_1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/pain5.wav")
|
|
{
|
|
sexedsound(self, "pain75_1.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/pain6.wav")
|
|
{
|
|
sexedsound(self, "pain100_1.wav");
|
|
return true;
|
|
}
|
|
|
|
if (soundname == "player/h2odeath.wav")
|
|
{
|
|
sexedsound(self, "drown.wav");
|
|
return true;
|
|
}
|
|
if (soundname == "player/death1.wav") //normal deaths come from the player animations.
|
|
return true;
|
|
if (soundname == "player/death2.wav")
|
|
return true;
|
|
if (soundname == "player/death3.wav")
|
|
return true;
|
|
if (soundname == "player/death4.wav")
|
|
return true;
|
|
if (soundname == "player/death5.wav")
|
|
return true;
|
|
|
|
return false;
|
|
};
|
|
|
|
//version that breaks when the ent is not necessarily known to us, called only when 'self' is a known CSQC entity.
|
|
float(float channel, string soundname, vector org, float vol, float attenuation, float flags) CSQC_ServerSound =
|
|
{
|
|
return ServerSoundStartRequest(channel, soundname, org, vol, attenuation, flags);
|
|
}
|
|
//version that lets the qc handle any ent fixups.
|
|
float(float sventnum, float channel, string soundname, float vol, float att, vector org, float pitchmod, float flags) CSQC_Event_Sound =
|
|
{
|
|
self = findfloat(world, entnum, sventnum);
|
|
if (self)
|
|
return ServerSoundStartRequest(channel, soundname, org, vol, att, flags);
|
|
return false;
|
|
};
|
|
#endif
|