url_fputs(fh, "#begin statsfile\n");
url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
#ifdef WATERMARK
- url_fputs(fh, strcat("#version ", WATERMARK(), "\n"));
+ url_fputs(fh, strcat("#version ", WATERMARK, "\n"));
#endif
url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n"));
url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n"));
.entity pusher;
.float pushltime;
+.float istypefrag;
+.float CopyBody_nextthink;
+.void(void) CopyBody_think;
void CopyBody_Think(void)
{
+ if(self.CopyBody_nextthink && time > self.CopyBody_nextthink)
+ {
+ self.CopyBody_think();
+ if(wasfreed(self))
+ return;
+ self.CopyBody_nextthink = self.nextthink;
+ self.CopyBody_think = self.think;
+ self.think = CopyBody_Think;
+ }
CSQCMODEL_AUTOUPDATE();
self.nextthink = time;
}
self.lip = oldself.lip;
self.colormap = oldself.colormap;
self.iscreature = oldself.iscreature;
+ self.teleportable = oldself.teleportable;
self.damagedbycontents = oldself.damagedbycontents;
self.angles = oldself.angles;
self.avelocity = oldself.avelocity;
self.effects = oldself.effects;
self.glowmod = oldself.glowmod;
self.event_damage = oldself.event_damage;
- self.animstate_startframe = oldself.animstate_startframe;
- self.animstate_numframes = oldself.animstate_numframes;
- self.animstate_framerate = oldself.animstate_framerate;
- self.animstate_starttime = oldself.animstate_starttime;
- self.animstate_endtime = oldself.animstate_endtime;
- self.animstate_override = oldself.animstate_override;
- self.animstate_looping = oldself.animstate_looping;
- self.frame = oldself.frame;
- self.dead_frame = oldself.dead_frame;
+ self.anim_state = oldself.anim_state;
+ self.anim_time = oldself.anim_time;
+ self.anim_lower_action = oldself.anim_lower_action;
+ self.anim_lower_time = oldself.anim_lower_time;
+ self.anim_upper_action = oldself.anim_upper_action;
+ self.anim_upper_time = oldself.anim_upper_time;
+ self.anim_implicit_state = oldself.anim_implicit_state;
+ self.anim_implicit_time = oldself.anim_implicit_time;
+ self.anim_lower_implicit_action = oldself.anim_lower_implicit_action;
+ self.anim_lower_implicit_time = oldself.anim_lower_implicit_time;
+ self.anim_upper_implicit_action = oldself.anim_upper_implicit_action;
+ self.anim_upper_implicit_time = oldself.anim_upper_implicit_time;
+ self.dphitcontentsmask = oldself.dphitcontentsmask;
+ self.death_time = oldself.death_time;
self.pain_finished = oldself.pain_finished;
self.health = oldself.health;
self.armorvalue = oldself.armorvalue;
if (keepvelocity == 1)
self.velocity = oldself.velocity;
self.oldvelocity = self.velocity;
+ self.alpha = oldself.alpha;
self.fade_time = oldself.fade_time;
self.fade_rate = oldself.fade_rate;
//self.weapon = oldself.weapon;
Drag_MoveDrag(oldself, self);
if(self.colormap <= maxclients && self.colormap > 0)
- self.colormap = 1024 + self.clientcolors;
+ self.colormap = 1024 + oldself.clientcolors;
CSQCMODEL_AUTOINIT();
+ self.CopyBody_nextthink = oldself.nextthink;
+ self.CopyBody_think = oldself.think;
self.nextthink = time;
self.think = CopyBody_Think;
+ // "bake" the current animation frame for clones (they don't get clientside animation)
+ animdecide_setframes(self, FALSE, frame, frame1time, frame2, frame2time);
self = oldself;
}
void player_setupanimsformodel()
{
- // defaults for legacy .zym models without animinfo files
- self.anim_die1 = animfixfps(self, '0 1 0.5'); // 2 seconds
- self.anim_die2 = animfixfps(self, '1 1 0.5'); // 2 seconds
- self.anim_draw = animfixfps(self, '2 1 3');
- // self.anim_duck = '3 1 100'; // This anim is broken, use slot 3 as a new free slot in the future ;)
- self.anim_duckwalk = animfixfps(self, '4 1 1');
- self.anim_duckjump = '5 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
- self.anim_duckidle = animfixfps(self, '6 1 1');
- self.anim_idle = animfixfps(self, '7 1 1');
- self.anim_jump = '8 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
- self.anim_pain1 = animfixfps(self, '9 1 2'); // 0.5 seconds
- self.anim_pain2 = animfixfps(self, '10 1 2'); // 0.5 seconds
- self.anim_shoot = animfixfps(self, '11 1 5'); // analyze models and set framerate
- self.anim_taunt = animfixfps(self, '12 1 0.33');
- self.anim_run = animfixfps(self, '13 1 1');
- self.anim_runbackwards = animfixfps(self, '14 1 1');
- self.anim_strafeleft = animfixfps(self, '15 1 1');
- self.anim_straferight = animfixfps(self, '16 1 1');
- self.anim_dead1 = animfixfps(self, '17 1 1');
- self.anim_dead2 = animfixfps(self, '18 1 1');
- self.anim_forwardright = animfixfps(self, '19 1 1');
- self.anim_forwardleft = animfixfps(self, '20 1 1');
- self.anim_backright = animfixfps(self, '21 1 1');
- self.anim_backleft = animfixfps(self, '22 1 1');
- self.anim_melee = animfixfps(self, '23 1 1');
- self.anim_duckwalkbackwards = animfixfps(self, '24 1 1');
- self.anim_duckwalkstrafeleft = animfixfps(self, '25 1 1');
- self.anim_duckwalkstraferight = animfixfps(self, '26 1 1');
- self.anim_duckwalkforwardright = animfixfps(self, '27 1 1');
- self.anim_duckwalkforwardleft = animfixfps(self, '28 1 1');
- self.anim_duckwalkbackright = animfixfps(self, '29 1 1');
- self.anim_duckwalkbackleft = animfixfps(self, '30 1 1');
- // TODO introspect models for finding right "fps" value (1/duration)
- // reset animstate now
- setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
+ // load animation info
+ animdecide_init(self);
+ animdecide_setstate(self, 0, FALSE);
}
void player_anim (void)
{
- updateanim(self);
- if (self.weaponentity)
- updateanim(self.weaponentity);
-
- if (self.deadflag != DEAD_NO)
- {
- if (time > self.animstate_endtime)
- {
- if (self.maxs_z > 5)
- {
- self.maxs_z = 5;
- setsize(self, self.mins, self.maxs);
- }
- self.frame = self.dead_frame;
- }
- return;
- }
-
- if (!self.animstate_override)
- {
- if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP)
- {
- if (self.crouch)
- {
- if (self.animstate_startframe != self.anim_duckjump_x) // don't perform another trace if already playing the crouch jump anim
- {
- traceline(self.origin + '0 0 1' * PL_CROUCH_MIN_z, self.origin + '0 0 1' * (PL_CROUCH_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
- if(!trace_startsolid && trace_fraction == 1 || !(self.animstate_startframe == self.anim_duckwalk_x || self.animstate_startframe == self.anim_duckidle_x)) // don't get stuck on non-crouch anims
- {
- setanim(self, self.anim_duckjump, FALSE, TRUE, self.restart_jump);
- self.restart_jump = FALSE;
- }
- }
- }
- else
- {
- if (self.animstate_startframe != self.anim_jump_x) // don't perform another trace if already playing the jump anim
- {
- traceline(self.origin + '0 0 1' * PL_MIN_z, self.origin + '0 0 1' * (PL_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
- if(!trace_startsolid && trace_fraction == 1 || self.animstate_startframe == self.anim_idle_x || (self.animstate_startframe == self.anim_melee_x && time - self.animstate_starttime >= 21/20)) // don't get stuck on idle animation in midair, nor melee after it finished
- {
- setanim(self, self.anim_jump, FALSE, TRUE, self.restart_jump);
- self.restart_jump = FALSE;
- }
- }
- }
- }
- else if (self.crouch)
- {
- if (self.movement_x > 0 && self.movement_y == 0)
- setanim(self, self.anim_duckwalk, TRUE, FALSE, FALSE);
- else if (self.movement_x < 0 && self.movement_y == 0)
- setanim(self, self.anim_duckwalkbackwards, TRUE, FALSE, FALSE);
- else if (self.movement_x == 0 && self.movement_y > 0)
- setanim(self, self.anim_duckwalkstraferight, TRUE, FALSE, FALSE);
- else if (self.movement_x == 0 && self.movement_y < 0)
- setanim(self, self.anim_duckwalkstrafeleft, TRUE, FALSE, FALSE);
- else if (self.movement_x > 0 && self.movement_y > 0)
- setanim(self, self.anim_duckwalkforwardright, TRUE, FALSE, FALSE);
- else if (self.movement_x > 0 && self.movement_y < 0)
- setanim(self, self.anim_duckwalkforwardleft, TRUE, FALSE, FALSE);
- else if (self.movement_x < 0 && self.movement_y > 0)
- setanim(self, self.anim_duckwalkbackright, TRUE, FALSE, FALSE);
- else if (self.movement_x < 0 && self.movement_y < 0)
- setanim(self, self.anim_duckwalkbackleft, TRUE, FALSE, FALSE);
- else
- setanim(self, self.anim_duckidle, TRUE, FALSE, FALSE);
- }
- else if ((self.movement_x * self.movement_x + self.movement_y * self.movement_y) > 20)
- {
- if (self.movement_x > 0 && self.movement_y == 0)
- setanim(self, self.anim_run, TRUE, FALSE, FALSE);
- else if (self.movement_x < 0 && self.movement_y == 0)
- setanim(self, self.anim_runbackwards, TRUE, FALSE, FALSE);
- else if (self.movement_x == 0 && self.movement_y > 0)
- setanim(self, self.anim_straferight, TRUE, FALSE, FALSE);
- else if (self.movement_x == 0 && self.movement_y < 0)
- setanim(self, self.anim_strafeleft, TRUE, FALSE, FALSE);
- else if (self.movement_x > 0 && self.movement_y > 0)
- setanim(self, self.anim_forwardright, TRUE, FALSE, FALSE);
- else if (self.movement_x > 0 && self.movement_y < 0)
- setanim(self, self.anim_forwardleft, TRUE, FALSE, FALSE);
- else if (self.movement_x < 0 && self.movement_y > 0)
- setanim(self, self.anim_backright, TRUE, FALSE, FALSE);
- else if (self.movement_x < 0 && self.movement_y < 0)
- setanim(self, self.anim_backleft, TRUE, FALSE, FALSE);
- else
- setanim(self, self.anim_run, TRUE, FALSE, FALSE);
- }
+ float deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
+ if(self.deadflag && !deadbits)
+ if(random() < 0.5)
+ deadbits = ANIMSTATE_DEAD1;
else
- setanim(self, self.anim_idle, TRUE, FALSE, FALSE);
- }
+ deadbits = ANIMSTATE_DEAD2;
+ float animbits = deadbits;
+ if(self.freezetag_frozen)
+ animbits |= ANIMSTATE_FROZEN;
+ if(self.crouch)
+ animbits |= ANIMSTATE_DUCK;
+ animdecide_setstate(self, animbits, FALSE);
+ animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND));
+
+#ifndef NO_LEGACY_NETWORKING
+ if(!self.iscsqcmodel)
+ animdecide_setframes(self, FALSE, frame, frame1time, frame2, frame2time);
+#endif
if (self.weaponentity)
- if (!self.weaponentity.animstate_override)
- setanim(self.weaponentity, self.weaponentity.anim_idle, TRUE, FALSE, FALSE);
+ {
+ updateanim(self.weaponentity);
+ if (!self.weaponentity.animstate_override)
+ setanim(self.weaponentity, self.weaponentity.anim_idle, TRUE, FALSE, FALSE);
+ }
}
void SpawnThrownWeapon (vector org, float w)
float j;
for(j = WEP_FIRST; j <= WEP_LAST; ++j)
{
- if(self.weapons & W_WeaponBit(j))
+ if(WEPSET_CONTAINS_EW(self, j))
if(W_IsWeaponThrowable(j))
W_ThrowNewWeapon(self, j, FALSE, org, randomvec() * 175 + '0 0 325');
}
self.dmg_take = self.dmg_take + take;//max(take - 10, 0);
self.dmg_inflictor = inflictor;
- if (self.health <= -autocvar_sv_gibhealth && !(self.effects & CSQCMODEL_EF_INVISIBLE))
+ if (self.health <= -autocvar_sv_gibhealth && self.alpha >= 0)
{
// don't use any animations as a gib
self.frame = 0;
- self.dead_frame = 0;
// view just above the floor
self.view_ofs = '0 0 4';
Violence_GibSplash(self, 1, 1, attacker);
- self.effects |= CSQCMODEL_EF_INVISIBLE;
+ self.alpha = -1;
self.solid = SOLID_NOT; // restore later
+ self.takedamage = DAMAGE_NO; // restore later
+ self.damagedbycontents = FALSE;
}
}
void ClientKill_Now_TeamChange();
-void freezetag_CheckWinner();
void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
float valid_damage_for_weaponstats;
float excess;
- if((g_arena && numspawned < 2) || (g_ca && !ca_teams_ok) && !inWarmupStage)
- return;
-
dh = max(self.health, 0);
da = max(self.armorvalue, 0);
take = damage;
}
+ if(attacker == self)
+ {
+ // don't reset pushltime for self damage as it may be an attempt to
+ // escape a lava pit or similar
+ //self.pushltime = 0;
+ self.istypefrag = 0;
+ }
+ else if(attacker.classname == "player")
+ {
+ self.pusher = attacker;
+ self.pushltime = time + autocvar_g_maxpushtime;
+ self.istypefrag = self.BUTTON_CHAT;
+ }
+ else if(time < self.pushltime)
+ {
+ attacker = self.pusher;
+ self.pushltime = max(self.pushltime, time + 0.6);
+ }
+ else
+ {
+ self.pushltime = 0;
+ self.istypefrag = 0;
+ }
+
frag_inflictor = inflictor;
frag_attacker = attacker;
frag_target = self;
if (!self.animstate_override)
{
if (random() > 0.5)
- setanim(self, self.anim_pain1, FALSE, TRUE, TRUE);
+ animdecide_setaction(self, ANIMACTION_PAIN1, TRUE);
else
- setanim(self, self.anim_pain2, FALSE, TRUE, TRUE);
+ animdecide_setaction(self, ANIMACTION_PAIN2, TRUE);
}
}
self.dmg_take = self.dmg_take + take;//max(take - 10, 0);
self.dmg_inflictor = inflictor;
- if(attacker == self)
- {
- // don't reset pushltime for self damage as it may be an attempt to
- // escape a lava pit or similar
- //self.pushltime = 0;
- }
- else if(attacker.classname == "player")
- {
- self.pusher = attacker;
- self.pushltime = time + autocvar_g_maxpushtime;
- }
- else if(time < self.pushltime)
- {
- attacker = self.pusher;
- self.pushltime = max(self.pushltime, time + 0.6);
- }
- else
- self.pushltime = 0;
+ if(g_ca && self != attacker && attacker.classname == "player")
+ PlayerScore_Add(attacker, SP_SCORE, (damage - excess) * autocvar_g_ca_damage2score_multiplier);
float abot, vbot, awep;
abot = (clienttype(attacker) == CLIENTTYPE_BOT);
vbot = (clienttype(self) == CLIENTTYPE_BOT);
valid_damage_for_weaponstats = 0;
+ awep = 0;
+
if(vbot || clienttype(self) == CLIENTTYPE_REAL)
if(abot || clienttype(attacker) == CLIENTTYPE_REAL)
if(attacker && self != attacker)
}
}
- if(!g_freezetag)
- {
- // become fully visible
- self.alpha = 1;
- // throw a weapon
- SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
- }
-
// print an obituary message
Obituary (attacker, inflictor, self, deathtype);
race_PreDie();
if(accuracy_isgooddamage(attacker, self))
attacker.accuracy.(accuracy_frags[w-1]) += 1;
- if(deathtype == DEATH_HURTTRIGGER && g_freezetag)
- {
- PutClientInServer();
- count_alive_players(); // re-count players
- freezetag_CheckWinner();
- return;
- }
-
frag_attacker = attacker;
frag_inflictor = inflictor;
frag_target = self;
+ frag_deathtype = deathtype;
MUTATOR_CALLHOOK(PlayerDies);
+
weapon_action(self.weapon, WR_PLAYERDEATH);
RemoveGrapplingHook(self);
- if(self.flagcarried)
- {
- if(attacker.classname != "player")
- DropFlag(self.flagcarried, self, attacker); // penalty for flag loss by suicide
- else if(attacker.team == self.team)
- DropFlag(self.flagcarried, attacker, attacker); // penalty for flag loss by suicide/teamkill
- else
- DropFlag(self.flagcarried, world, attacker);
- }
- if(self.ballcarried && g_nexball)
- DropBall(self.ballcarried, self.origin, self.velocity);
Portal_ClearAllLater(self);
if(clienttype(self) == CLIENTTYPE_REAL)
//WriteAngle (MSG_ONE, 80);
}
- if(defer_ClientKill_Now_TeamChange) // TODO does this work with FreezeTag?
- ClientKill_Now_TeamChange();
+ if(defer_ClientKill_Now_TeamChange)
+ ClientKill_Now_TeamChange(); // can turn player into spectator
- if(g_arena)
- Spawnqueue_Unmark(self);
-
- if(g_freezetag)
+ // player could have been miraculously resuscitated ;)
+ // e.g. players in freezetag get frozen, they don't really die
+ if(self.health >= 1 || self.classname != "player")
return;
// when we get here, player actually dies
- // clear waypoints (do this AFTER FreezeTag)
+
+ // clear waypoints
WaypointSprite_PlayerDead();
+ // throw a weapon
+ SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
+ // become fully visible
+ self.alpha = default_player_alpha;
// make the corpse upright (not tilted)
self.angles_x = 0;
self.angles_z = 0;
if(!waves)
waves = autocvar_g_respawn_waves;
if(waves)
- self.death_time = ceil((time + sdelay) / waves) * waves;
+ self.respawn_time = ceil((time + sdelay) / waves) * waves;
else
- self.death_time = time + sdelay;
- if((sdelay + waves >= 5.0) && (self.death_time - time > 1.75))
+ self.respawn_time = time + sdelay;
+ if((sdelay + waves >= 5.0) && (self.respawn_time - time > 1.75))
self.respawn_countdown = 10; // first number to count down from is 10
else
self.respawn_countdown = -1; // do not count down
+ self.death_time = time;
if (random() < 0.5)
- {
- setanim(self, self.anim_die1, FALSE, TRUE, TRUE);
- self.dead_frame = self.anim_dead1_x;
- }
+ animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD1, TRUE);
else
+ animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD2, TRUE);
+ if (self.maxs_z > 5)
{
- setanim(self, self.anim_die2, FALSE, TRUE, TRUE);
- self.dead_frame = self.anim_dead2_x;
+ self.maxs_z = 5;
+ setsize(self, self.mins, self.maxs);
}
// set damage function to corpse damage
self.event_damage = PlayerCorpseDamage;
// 0 = reject
// -1 = fake accept
{
- string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr, privatemsgprefix;
- float flood, privatemsgprefixlen;
+ string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr;
+ float flood;
var .float flood_field;
entity head;
float ret;
-
- if(Ban_MaybeEnforceBan(source))
- return 0;
+ string privatemsgprefix = string_null; float privatemsgprefixlen = 0;
if(!teamsay && !privatesay)
if(substring(msgin, 0, 1) == " ")
else if(teamplay)
colorstr = Team_ColorCode(source.team);
else
+ {
+ colorstr = "";
teamsay = FALSE;
+ }
if(intermission_running)
teamsay = FALSE;
// FLOOD CONTROL
flood = 0;
+ flood_field = floodcontrol_chat;
if(floodcontrol)
{
float flood_spl;
flood = 1;
}
- if (timeoutStatus == 2) //when game is paused, no flood protection
+ if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection
source.flood_field = flood = 0;
}
if(!privatesay)
if(source.classname != "player")
{
- if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !inWarmupStage))
- teamsay = -1; // spectators
+ if not(intermission_running)
+ if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(inWarmupStage || gameover)))
+ teamsay = -1; // spectators
}
if(flood)
if(sourcemsgstr != "" && ret != 0)
{
- if(ret < 0) // fake
+ if(ret < 0) // faked message, because the player is muted
{
sprint(source, sourcemsgstr);
if(sourcecmsgstr != "" && !privatesay)
centerprint(source, sourcecmsgstr);
}
- else if(privatesay)
+ else if(privatesay) // private message, between 2 people only, not sent to server console
{
sprint(source, sourcemsgstr);
sprint(privatesay, msgstr);
if(cmsgstr != "")
centerprint(privatesay, cmsgstr);
}
- else if(teamsay > 0)
+ else if(teamsay > 0) // team message, only sent to team mates
{
sprint(source, sourcemsgstr);
+ //print(msgstr); // send to server console too
if(sourcecmsgstr != "")
centerprint(source, sourcecmsgstr);
FOR_EACH_REALPLAYER(head) if(head.team == source.team)
centerprint(head, cmsgstr);
}
}
- else if(teamsay < 0)
+ else if(teamsay < 0) // spectator message, only sent to spectators
{
sprint(source, sourcemsgstr);
+ //print(msgstr); // send to server console too
FOR_EACH_REALCLIENT(head) if(head.classname != "player")
if(head != source)
sprint(head, msgstr);
}
- else if(sourcemsgstr != msgstr)
+ else if(sourcemsgstr != msgstr) // trimmed/server fixed message, sent to all players
{
sprint(source, sourcemsgstr);
+ //print(msgstr); // send to server console too
FOR_EACH_REALCLIENT(head)
if(head != source)
sprint(head, msgstr);
}
else
- bprint(msgstr);
+ bprint(msgstr); // entirely normal message, sent to all players -- bprint sends to server console too.
}
return ret;
case VOICETYPE_TAUNT:
if(self.classname == "player")
if(self.deadflag == DEAD_NO)
- setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
+ animdecide_setaction(self, ANIMACTION_TAUNT, TRUE);
if(!sv_taunt)
break;
if(sv_gentle)
case VOICETYPE_TAUNT:
if(self.classname == "player")
if(self.deadflag == DEAD_NO)
- setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
+ animdecide_setaction(self, ANIMACTION_TAUNT, TRUE);
if(!sv_taunt)
break;
if(sv_gentle)