Xonotic
player.qc
Go to the documentation of this file.
1 #include "player.qh"
2 
3 #include <common/anim.qh>
4 #include <common/animdecide.qh>
7 #include <common/effects/all.qh>
17 #include <common/physics/player.qh>
18 #include <common/playerstats.qh>
19 #include <common/state.qh>
20 #include <common/wepent.qh>
22 #include <server/bot/api.qh>
23 #include <server/cheats.qh>
24 #include <server/client.qh>
25 #include <server/clientkill.qh>
26 #include <server/command/common.qh>
27 #include <server/command/vote.qh>
28 #include <server/damage.qh>
29 #include <server/handicap.qh>
30 #include <server/hook.qh>
31 #include <server/main.qh>
32 #include <server/mutators/_mod.qh>
33 #include <server/portals.qh>
34 #include <server/teamplay.qh>
36 #include <server/weapons/common.qh>
40 #include <server/world.qh>
41 
43 {
44  // called when the player has become stuck or frozen
45  // so objective items aren't stuck with the player
46 
47  MUTATOR_CALLHOOK(DropSpecialItems, player);
48 }
49 
51 {
52  if(this.CopyBody_nextthink && time > this.CopyBody_nextthink)
53  {
54  this.CopyBody_think(this);
55  if(wasfreed(this))
56  return;
57  this.CopyBody_nextthink = this.nextthink;
58  this.CopyBody_think = getthink(this);
59  setthink(this, CopyBody_Think);
60  }
61  CSQCMODEL_AUTOUPDATE(this);
62  this.nextthink = time;
63 }
64 void CopyBody(entity this, float keepvelocity)
65 {
66  if (this.effects & EF_NODRAW)
67  return;
68  entity clone = new(body);
69  clone.enemy = this;
70  clone.lip = this.lip;
71  clone.colormap = this.colormap;
72  clone.iscreature = this.iscreature;
73  clone.teleportable = this.teleportable;
74  clone.damagedbycontents = this.damagedbycontents;
75  if(clone.damagedbycontents)
77  clone.angles = this.angles;
78  clone.v_angle = this.v_angle;
79  clone.avelocity = this.avelocity;
80  clone.damageforcescale = this.damageforcescale;
81  clone.effects = this.effects;
82  clone.glowmod = this.glowmod;
83  clone.event_damage = this.event_damage;
84  clone.event_heal = this.event_heal;
85  clone.anim_state = this.anim_state;
86  clone.anim_time = this.anim_time;
87  clone.anim_lower_action = this.anim_lower_action;
88  clone.anim_lower_time = this.anim_lower_time;
89  clone.anim_upper_action = this.anim_upper_action;
90  clone.anim_upper_time = this.anim_upper_time;
91  clone.anim_implicit_state = this.anim_implicit_state;
92  clone.anim_implicit_time = this.anim_implicit_time;
93  clone.anim_lower_implicit_action = this.anim_lower_implicit_action;
94  clone.anim_lower_implicit_time = this.anim_lower_implicit_time;
95  clone.anim_upper_implicit_action = this.anim_upper_implicit_action;
96  clone.anim_upper_implicit_time = this.anim_upper_implicit_time;
97  clone.dphitcontentsmask = this.dphitcontentsmask;
98  clone.death_time = this.death_time;
99  clone.pain_finished = this.pain_finished;
101  SetResourceExplicit(clone, RES_ARMOR, GetResource(this, RES_ARMOR));
102  clone.armortype = this.armortype;
103  clone.model = this.model;
104  clone.modelindex = this.modelindex;
105  clone.skin = this.skin;
106  clone.species = this.species;
107  clone.move_qcphysics = false; // don't run gamecode logic on clones, too many
108  set_movetype(clone, this.move_movetype);
109  clone.solid = this.solid;
110  clone.takedamage = this.takedamage;
111  setcefc(clone, getcefc(this));
112  clone.uncustomizeentityforclient = this.uncustomizeentityforclient;
113  clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set;
114  if (keepvelocity == 1)
115  clone.velocity = this.velocity;
116  clone.oldvelocity = clone.velocity;
117  clone.alpha = this.alpha;
118  clone.fade_time = this.fade_time;
119  clone.fade_rate = this.fade_rate;
120  //clone.weapon = this.weapon;
121  setorigin(clone, this.origin);
122  setsize(clone, this.mins, this.maxs);
123  clone.reset = SUB_Remove;
124  clone._ps = this._ps;
125 
126  Drag_MoveDrag(this, clone);
127 
128  if(clone.colormap <= maxclients && clone.colormap > 0)
129  clone.colormap = 1024 + this.clientcolors;
130 
131  CSQCMODEL_AUTOINIT(clone);
132  clone.CopyBody_nextthink = this.nextthink;
133  clone.CopyBody_think = getthink(this);
134  clone.nextthink = time;
135  setthink(clone, CopyBody_Think);
136  // "bake" the current animation frame for clones (they don't get clientside animation)
139 
140  IL_PUSH(g_clones, clone);
141 
142  MUTATOR_CALLHOOK(CopyBody, this, clone, keepvelocity);
143 }
144 
146 {
147  // load animation info
149  animdecide_setstate(this, 0, false);
150 }
151 
152 void player_anim(entity this)
153 {
154  int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
155  if(IS_DEAD(this)) {
156  if (!deadbits) {
157  // Decide on which death animation to use.
158  if(random() < 0.5)
159  deadbits = ANIMSTATE_DEAD1;
160  else
161  deadbits = ANIMSTATE_DEAD2;
162  }
163  } else {
164  // Clear a previous death animation.
165  deadbits = 0;
166  }
167  int animbits = deadbits;
168  if(STAT(FROZEN, this))
169  animbits |= ANIMSTATE_FROZEN;
170  if(this.move_movetype == MOVETYPE_FOLLOW)
171  animbits |= ANIMSTATE_FOLLOW;
172  if(IS_DUCKED(this))
173  animbits |= ANIMSTATE_DUCK;
174  animdecide_setstate(this, animbits, false);
176 }
177 
178 void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
179 {
180  float take, save;
181  vector v;
182  Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
183 
184  v = healtharmor_applydamage(GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
185  take = v.x;
186  save = v.y;
187 
188  if(sound_allowed(MSG_BROADCAST, attacker))
189  {
190  if (save > 10)
191  sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
192  else if (take > 30)
193  sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
194  else if (take > 10)
195  sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM);
196  }
197 
198  if (take > 50)
199  Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker);
200  if (take > 100)
201  Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
202 
203  TakeResource(this, RES_ARMOR, save);
204  TakeResource(this, RES_HEALTH, take);
205  // pause regeneration for 5 seconds
207 
208  this.dmg_save = this.dmg_save + save;//max(save - 10, 0);
209  this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
210  this.dmg_inflictor = inflictor;
211 
212  if (GetResource(this, RES_HEALTH) <= -autocvar_sv_gibhealth && this.alpha >= 0)
213  {
214  // don't use any animations as a gib
215  this.frame = 0;
216  // view just above the floor
217  this.view_ofs = '0 0 4';
218 
219  Violence_GibSplash(this, 1, 1, attacker);
220  this.alpha = -1;
221  this.solid = SOLID_NOT; // restore later
222  this.takedamage = DAMAGE_NO; // restore later
223  if(this.damagedbycontents)
225  this.damagedbycontents = false;
226  }
227 }
228 
229 void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
230 {
231  vector v;
232  float initial_health = max(GetResource(this, RES_HEALTH), 0);
233  float initial_armor = max(GetResource(this, RES_ARMOR), 0);
234  float take = 0, save = 0;
235 
236  if (damage)
237  {
238  if(!DEATH_ISSPECIAL(deathtype))
239  {
240  damage *= Handicap_GetTotalHandicap(this);
241  if (this != attacker && IS_PLAYER(attacker))
242  {
243  damage /= Handicap_GetTotalHandicap(attacker);
244  }
245  }
246 
247  if (STAT(FROZEN, this))
248  {
249  if (!ITEM_DAMAGE_NEEDKILL(deathtype))
250  damage = 0;
251  }
252  else if (StatusEffects_active(STATUSEFFECT_SpawnShield, this) && autocvar_g_spawnshield_blockdamage < 1)
253  damage *= 1 - bound(0, autocvar_g_spawnshield_blockdamage, 1);
254 
255  if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
256  {
257  vector ear1, ear2;
258  vector d;
259  float f;
260  ear1 = this.origin;
261  ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8
262  ear2 = ear1;
263  makevectors(this.angles);
264  ear1 += v_right * -10;
265  ear2 += v_right * +10;
266  d = inflictor.origin - this.origin;
267  if (d)
268  f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right!
269  else
270  f = 0; // Assum ecenter.
271  force = v_right * vlen(force);
272  Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker);
273  Violence_GibSplash_At(ear2, force, 2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker);
274  if(f > 0)
275  {
276  hitloc = ear1;
277  force = force * -1;
278  }
279  else
280  {
281  hitloc = ear2;
282  // force is already good
283  }
284  }
285  else
286  Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
287 
288  v = healtharmor_applydamage(GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
289  take = v.x;
290  save = v.y;
291  }
292 
293  if(attacker == this)
294  {
295  // don't reset pushltime for self damage as it may be an attempt to
296  // escape a lava pit or similar
297  //this.pushltime = 0;
298  this.istypefrag = 0;
299  }
300  else if(IS_PLAYER(attacker))
301  {
302  this.pusher = attacker;
304  this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this);
305  }
306  else if(time < this.pushltime)
307  {
308  attacker = this.pusher;
309  this.pushltime = max(this.pushltime, time + 0.6);
310  }
311  else
312  {
313  this.pushltime = 0;
314  this.istypefrag = 0;
315  }
316 
317  MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage);
318  take = bound(0, M_ARGV(4, float), GetResource(this, RES_HEALTH));
319  save = bound(0, M_ARGV(5, float), GetResource(this, RES_ARMOR));
320  float excess = max(0, damage - take - save);
321 
322  if(sound_allowed(MSG_BROADCAST, attacker))
323  {
324  if (save > 10 && (initial_health - take) > 0) // don't play armor sound if the attack is fatal
325  sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
326  else if (take > 30)
327  sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
328  else if (take > 10)
329  sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them?
330  }
331 
332  if (take > 50)
333  Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker);
334  if (take > 100)
335  Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
336 
337  if (!StatusEffects_active(STATUSEFFECT_SpawnShield, this) || autocvar_g_spawnshield_blockdamage < 1)
338  {
339  if (!(this.flags & FL_GODMODE))
340  {
341  TakeResource(this, RES_ARMOR, save);
342  TakeResource(this, RES_HEALTH, take);
343  // pause regeneration for 5 seconds
344  if(take)
346 
347  if (time > this.pain_finished && !STAT(FROZEN, this)) // Don't switch pain sequences like crazy
348  {
349  this.pain_finished = time + 0.5; //Supajoe
350 
351  if(autocvar_sv_gentle < 1) {
352  if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
353  {
354  if (!this.animstate_override)
355  {
356  if (random() > 0.5)
358  else
360  }
361  }
362  float myhp = GetResource(this, RES_HEALTH);
363  if(myhp > 1)
364  if(myhp < 25 || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || take > 20 || attacker != this)
365  if(sound_allowed(MSG_BROADCAST, attacker))
366  // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
367  {
368  if(deathtype == DEATH_FALL.m_id)
369  PlayerSound(this, playersound_fall, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
370  else if(myhp > 75)
371  PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
372  else if(myhp > 50)
373  PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
374  else if(myhp > 25)
375  PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
376  else
377  PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
378  }
379  }
380  }
381 
382  // throw off bot aim temporarily
383  float shake;
384  if(IS_BOT_CLIENT(this) && GetResource(this, RES_HEALTH) >= 1)
385  {
386  shake = damage * 5 / (bound(0,skill,100) + 1);
387  this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake;
388  this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake;
389  this.v_angle_x = bound(-90, this.v_angle.x, 90);
390  }
391  }
392  else
393  this.max_armorvalue += (save + take);
394  }
395  this.dmg_save = this.dmg_save + save;//max(save - 10, 0);
396  this.dmg_take = this.dmg_take + take;//max(take - 10, 0);
397  this.dmg_inflictor = inflictor;
398 
399  bool abot = (IS_BOT_CLIENT(attacker));
400  bool vbot = (IS_BOT_CLIENT(this));
401 
402  bool valid_damage_for_weaponstats = false;
403  Weapon awep = WEP_Null;
404 
405  if (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime)
406  if(vbot || IS_REAL_CLIENT(this))
407  if(abot || IS_REAL_CLIENT(attacker))
408  if(attacker && this != attacker)
409  if (DIFF_TEAM(this, attacker) && (!STAT(FROZEN, this) || this.freeze_time > time))
410  {
411  if(DEATH_ISSPECIAL(deathtype))
412  awep = attacker.(weaponentity).m_weapon;
413  else
414  awep = DEATH_WEAPONOF(deathtype);
415  valid_damage_for_weaponstats = true;
416  }
417 
418  float dh = initial_health - max(GetResource(this, RES_HEALTH), 0); // health difference
419  float da = initial_armor - max(GetResource(this, RES_ARMOR), 0); // armor difference
420  if(valid_damage_for_weaponstats)
421  {
422  WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da);
423  }
424 
425  bool forbid_logging_damage = MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage);
426 
427  if ((dh || da) && !forbid_logging_damage)
428  {
429  float realdmg = damage - excess;
430  if ((this != attacker || deathtype == DEATH_KILL.m_id) && realdmg && !STAT(FROZEN, this)
431  && (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime))
432  {
433  if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this) && deathtype != DEATH_KILL.m_id)
434  GameRules_scoring_add(attacker, DMG, realdmg);
435  if (IS_PLAYER(this))
436  GameRules_scoring_add(this, DMGTAKEN, realdmg);
437  }
438  }
439 
440  if (GetResource(this, RES_HEALTH) < 1)
441  {
442  bool defer_ClientKill_Now_TeamChange = false;
443 
444  if(this.alivetime)
445  {
446  PlayerStats_GameReport_Event_Player(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
447  this.alivetime = 0;
448  }
449 
450  if(valid_damage_for_weaponstats)
451  WeaponStats_LogKill(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot);
452 
453  if(autocvar_sv_gentle < 1)
454  if(sound_allowed(MSG_BROADCAST, attacker))
455  {
456  if(deathtype == DEATH_DROWN.m_id)
457  PlayerSound(this, playersound_drown, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
458  else
459  PlayerSound(this, playersound_death, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND);
460  }
461 
462  // get rid of kill indicator
463  if(this.killindicator)
464  {
465  delete(this.killindicator);
466  this.killindicator = NULL;
467  if(this.killindicator_teamchange)
468  defer_ClientKill_Now_TeamChange = true;
469 
470  if(this.classname == "body")
471  if(deathtype == DEATH_KILL.m_id)
472  {
473  // for the lemmings fans, a small harmless explosion
474  Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
475  }
476  }
477 
478  // print an obituary message
479  if(this.classname != "body")
480  Obituary(attacker, inflictor, this, deathtype, weaponentity);
481 
482  // increment frag counter for used weapon type
483  Weapon w = DEATH_WEAPONOF(deathtype);
484  if(w != WEP_Null && accuracy_isgooddamage(attacker, this))
485  CS(attacker).accuracy.(accuracy_frags[w.m_id-1]) += 1;
486 
487  this.respawn_time = 0;
488  MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
489  damage = M_ARGV(4, float);
490  excess = max(0, damage - take - save);
491 
492  //Weapon wep = this.(weaponentity).m_weapon;
493  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
494  {
495  .entity went = weaponentities[slot];
496  if(!this.(went))
497  continue; // TODO: clones have no weapon, but we don't want to have to check this all the time
498  Weapon wep = this.(went).m_weapon;
499  wep.wr_playerdeath(wep, this, went);
500  }
501 
502  RemoveGrapplingHooks(this);
503 
504  Portal_ClearAllLater(this);
505 
506  this.fixangle = true;
507 
508  if(defer_ClientKill_Now_TeamChange)
509  ClientKill_Now_TeamChange(this); // can turn player into spectator
510 
511  // player could have been miraculously resuscitated ;)
512  // e.g. players in freezetag get frozen, they don't really die
513  if(GetResource(this, RES_HEALTH) >= 1 || !(IS_PLAYER(this) || this.classname == "body"))
514  return;
515 
516  if (!this.respawn_time) // can be set in the mutator hook PlayerDies
518 
519  // when we get here, player actually dies
520 
521  Unfreeze(this, false); // remove any icy remains
522 
523  // clear waypoints
524  WaypointSprite_PlayerDead(this);
525  // throw a weapon
526  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
527  {
528  .entity went = weaponentities[slot];
529  SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, this.(went).m_weapon, went);
530  }
531 
532  // become fully visible
534  // make the corpse upright (not tilted)
535  this.angles_x = 0;
536  this.angles_z = 0;
537  // don't spin
538  this.avelocity = '0 0 0';
539  // view from the floor
540  this.view_ofs = '0 0 -8';
541  if(this.move_movetype == MOVETYPE_NOCLIP)
542  {
543  // don't toss the corpse in this case, it can get stuck in solid (causing low fps)
544  // or fall indefinitely into the void if out of the map
545  this.velocity = '0 0 0';
546  }
547  else
548  {
549  // toss the corpse
551  }
552  // shootable corpse
553  this.solid = SOLID_CORPSE;
554  PS(this).ballistics_density = autocvar_g_ballistics_density_corpse;
555  // don't stick to the floor
556  UNSET_ONGROUND(this);
557  // dying animation
558  this.deadflag = DEAD_DYING;
559  // don't play teleportation sounds
561 
562  STAT(AIR_FINISHED, this) = 0;
563 
564  this.death_time = time;
565  if (random() < 0.5)
566  animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true);
567  else
568  animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true);
569 
570  // set damage function to corpse damage
571  this.event_damage = PlayerCorpseDamage;
572  this.event_heal = func_null;
573  // call the corpse damage function just in case it wants to gib
574  this.event_damage(this, inflictor, attacker, excess, deathtype, weaponentity, hitloc, force);
575 
576  // set up to fade out later
577  SUB_SetFade (this, time + 6 + random (), 1);
578  // reset body think wrapper broken by SUB_SetFade
579  if(this.classname == "body" && getthink(this) != CopyBody_Think) {
580  this.CopyBody_think = getthink(this);
581  this.CopyBody_nextthink = this.nextthink;
582  setthink(this, CopyBody_Think);
583  this.nextthink = time;
584  }
585 
586  if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") {
587  // remove corpse
588  // clones don't run any animation code any more, so we must gib them when they die :(
589  this.event_damage(this, inflictor, attacker, autocvar_sv_gibhealth + 1, deathtype, weaponentity, hitloc, force);
590  }
591 
592  // reset fields the weapons may use just in case
593  if(this.classname != "body")
594  {
595  FOREACH(Weapons, it != WEP_Null,
596  {
597  it.wr_resetplayer(it, this);
598  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
599  {
600  ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0;
601  }
602  });
603  }
604  MUTATOR_CALLHOOK(PlayerDied, this);
605  }
606 }
607 
608 bool PlayerHeal(entity targ, entity inflictor, float amount, float limit)
609 {
610  if(GetResource(targ, RES_HEALTH) <= 0 || GetResource(targ, RES_HEALTH) >= limit)
611  return false;
612 
613  GiveResourceWithLimit(targ, RES_HEALTH, amount, limit);
614  return true;
615 }
616 
617 void precache_playermodel(string m)
618 {
619  int globhandle, i, n;
620  string f;
621 
622  // remove :<skinnumber> suffix
623  int j = strstrofs(m, ":", 0);
624  if(j >= 0)
625  m = substring(m, 0, j);
626 
627  if(substring(m, -9, 5) == "_lod1")
628  return;
629  if(substring(m, -9, 5) == "_lod2")
630  return;
631  precache_model(m);
632  f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
633  if(fexists(f))
634  precache_model(f);
635  f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
636  if(fexists(f))
637  precache_model(f);
638 
639  globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
640  if (globhandle < 0)
641  return;
642  n = search_getsize(globhandle);
643  for (i = 0; i < n; ++i)
644  {
645  //print(search_getfilename(globhandle, i), "\n");
646  f = search_getfilename(globhandle, i);
648  }
649  search_end(globhandle);
650 }
651 void precache_all_playermodels(string pattern)
652 {
653  int globhandle = search_begin(pattern, true, false);
654  if (globhandle < 0) return;
655  int n = search_getsize(globhandle);
656  for (int i = 0; i < n; ++i)
657  {
658  string s = search_getfilename(globhandle, i);
660  }
661  search_end(globhandle);
662 }
663 
664 void precache_playermodels(string s)
665 {
666  FOREACH_WORD(s, true, { precache_playermodel(it); });
667 }
668 
669 PRECACHE(PlayerModels)
670 {
671  // Precache all player models if desired
673  {
674  PrecachePlayerSounds("sound/player/default.sounds");
675  precache_all_playermodels("models/player/*.zym");
676  precache_all_playermodels("models/player/*.dpm");
677  precache_all_playermodels("models/player/*.md3");
678  precache_all_playermodels("models/player/*.psk");
679  precache_all_playermodels("models/player/*.iqm");
680  }
681 
683  {
689  }
690 }
const float SOLID_NOT
Definition: csprogsdefs.qc:244
float DEAD_DYING
Definition: progsdefs.qc:275
ERASEABLE void IL_REMOVE(IntrusiveList this, entity it)
Remove any element, anywhere in the list.
#define round_handler_IsActive()
bool PlayerHeal(entity targ, entity inflictor, float amount, float limit)
Definition: player.qc:608
float dmg_take
Definition: view.qh:119
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
const int HITTYPE_SOUND
Definition: all.qh:30
float colormap
Definition: csprogsdefs.qc:131
const int ANIMSTATE_FROZEN
Definition: animdecide.qh:128
float alpha
Definition: items.qc:14
float anim_upper_action
Definition: animdecide.qh:112
skin
Definition: ent_cs.qc:143
void Portal_ClearAllLater(entity own)
Definition: portals.qc:610
vector view_ofs
Definition: progsdefs.qc:151
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
Definition: cl_resources.qc:15
const int CH_PAIN
Definition: sound.qh:18
#define getthink(e)
float autocvar_g_balance_pause_health_regen
Definition: sv_resources.qh:35
void PrecachePlayerSounds(string f)
Definition: globalsound.qc:222
void animdecide_setframes(entity e, float support_blending,.float fld_frame,.float fld_frame1time,.float fld_frame2,.float fld_frame2time)
Definition: animdecide.qc:296
const int VOICETYPE_PLAYERSOUND
Definition: globalsound.qh:64
void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot)
Definition: weaponstats.qc:108
const int TELEPORT_SIMPLE
Definition: teleporters.qh:27
void animdecide_setstate(entity e, int newstate, float restart)
Definition: animdecide.qc:330
bool animstate_override
true for one cycle, then changed to false
Definition: anim.qh:42
void precache_playermodels(string s)
Definition: player.qc:664
bool iscreature
Definition: main.qh:42
Header file that describes the handicap system.
float modelindex
Definition: csprogsdefs.qc:91
float frame2
secondary framegroup animation (strength = lerpfrac)
Definition: anim.qh:8
float respawn_time
Definition: client.qh:325
#define IS_DUCKED(s)
Definition: player.qh:206
float MOVETYPE_TOSS
Definition: progsdefs.qc:252
float anim_lower_action
Definition: animdecide.qh:110
float damageforcescale
Definition: damage.qh:137
fade_rate
Definition: projectile.qh:14
IntrusiveList g_damagedbycontents
Definition: damage.qh:155
entity() spawn
void Obituary(entity attacker, entity inflictor, entity targ, int deathtype,.entity weaponentity)
Definition: damage.qc:229
void animdecide_load_if_needed(entity e)
Definition: animdecide.qc:53
void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: player.qc:229
ClientState CS(Client this)
Definition: state.qh:47
vector v_angle
Definition: progsdefs.qc:161
#define IS_ONGROUND(s)
Definition: movetypes.qh:16
#define GameRules_scoring_add(client, fld, value)
Definition: sv_rules.qh:78
float skill
Definition: api.qh:35
vector maxs
Definition: csprogsdefs.qc:113
string autocvar_sv_defaultplayermodel
Definition: player.qh:13
float CopyBody_nextthink
Definition: player.qh:27
#define PS(this)
Definition: state.qh:18
int killindicator_teamchange
Definition: clientkill.qh:8
PlayerState _ps
Definition: state.qh:17
float anim_lower_implicit_time
Definition: animdecide.qh:119
float pushltime
Definition: jumppads.qh:10
float maxclients
Definition: csprogsdefs.qc:21
float frame2time
start time of framegroup animation
Definition: anim.qh:24
float species
Definition: main.qh:43
origin
Definition: ent_cs.qc:114
float FL_GODMODE
Definition: progsdefs.qc:237
string classname
Definition: csprogsdefs.qc:107
#define round_handler_IsRoundStarted()
vector avelocity
Definition: csprogsdefs.qc:105
string autocvar_sv_defaultplayermodel_blue
Definition: player.qh:14
#define DIFF_TEAM(a, b)
Definition: teams.qh:240
int anim_implicit_state
Definition: animdecide.qh:116
const int ANIMACTION_PAIN2
Definition: animdecide.qh:144
#define UNSET_ONGROUND(s)
Definition: movetypes.qh:18
float anim_upper_time
Definition: animdecide.qh:113
float effects
Definition: csprogsdefs.qc:111
float spawnflags
Definition: progsdefs.qc:191
float autocvar_g_maxpushtime
Definition: damage.qh:17
string model
Definition: csprogsdefs.qc:108
float move_movetype
Definition: movetypes.qh:76
#define DEATH_WEAPONOF(t)
Definition: all.qh:41
entity pusher
Definition: teleporters.qh:13
const int ANIMSTATE_FOLLOW
Definition: animdecide.qh:129
#define FOREACH_WORD(words, cond, body)
Definition: iter.qh:33
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
Definition: cl_resources.qc:31
#define ATTACK_FINISHED_FOR(ent, w, slot)
Definition: weaponsystem.qh:38
RES_HEALTH
Definition: ent_cs.qc:126
const float EF_NODRAW
Definition: csprogsdefs.qc:305
float anim_upper_implicit_time
Definition: animdecide.qh:121
void Unfreeze(entity targ, bool reset_health)
Definition: damage.qc:546
vector mins
Definition: csprogsdefs.qc:113
void precache_playermodel(string m)
Definition: player.qc:617
float dmg_save
Definition: progsdefs.qc:199
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
void GiveResourceWithLimit(entity receiver, Resource res_type, float amount, float limit)
Gives an entity some resource but not more than a limit.
float damagedbycontents
Definition: damage.qh:48
entity dmg_inflictor
Definition: progsdefs.qc:200
string autocvar_sv_defaultplayermodel_yellow
Definition: player.qh:17
IntrusiveList g_clones
Definition: player.qh:44
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
const int MAX_WEAPONSLOTS
Definition: weapon.qh:13
float autocvar_g_ballistics_density_corpse
Definition: tracing.qh:6
void animdecide_setimplicitstate(entity e, float onground)
Definition: animdecide.qc:248
void animdecide_setaction(entity e, float action, float restart)
Definition: animdecide.qc:338
float armortype
Definition: progsdefs.qc:178
void player_anim(entity this)
Definition: player.qc:152
float fixangle
Definition: progsdefs.qc:160
float freeze_time
Definition: damage.qh:115
float Handicap_GetTotalHandicap(entity player)
Returns the total handicap of the player.
Definition: handicap.qc:37
#define NULL
Definition: post.qh:17
const float VOL_BASE
Definition: sound.qh:36
void ClientKill_Now_TeamChange(entity this)
Definition: clientkill.qc:18
void player_setupanimsformodel(entity this)
Definition: player.qc:145
#define strstrofs
Definition: dpextensions.qh:42
float takedamage
Definition: progsdefs.qc:147
float max_armorvalue
Definition: items.qh:31
bool autocvar_sv_defaultcharacter
Definition: player.qh:6
int anim_state
Definition: animdecide.qh:108
float alivetime
Definition: client.qh:66
#define M_ARGV(x, type)
Definition: events.qh:17
void SpawnThrownWeapon(entity this, vector org, Weapon wep,.entity weaponentity)
Definition: throwing.qc:180
#define IS_DEAD(s)
Definition: utils.qh:26
const float ATTEN_NORM
Definition: sound.qh:30
float nextthink
Definition: csprogsdefs.qc:121
const int CH_SHOTS
Definition: sound.qh:14
void RemoveGrapplingHooks(entity pl)
Definition: hook.qc:78
const int ANIMACTION_PAIN1
Definition: animdecide.qh:143
vector(float skel, float bonenum) _skel_get_boneabs_hidden
const int WEP_FLAG_CANCLIMB
Definition: weapon.qh:198
#define ITEM_DAMAGE_NEEDKILL(dt)
Definition: items.qh:130
entity killindicator
Definition: clientkill.qh:7
float MOVETYPE_NOCLIP
Definition: progsdefs.qc:254
vector v
Definition: ent_cs.qc:116
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
float deadflag
Definition: progsdefs.qc:149
float pain_finished
float anim_lower_implicit_action
Definition: animdecide.qh:118
float flags
Definition: csprogsdefs.qc:129
float autocvar_g_spawnshield_blockdamage
Definition: player.qh:4
float anim_lower_time
Definition: animdecide.qh:111
bool istypefrag
Definition: jumppads.qh:11
float MOVETYPE_FOLLOW
float lip
Definition: subs.qh:40
float fade_time
Definition: common.qh:22
void calculate_player_respawn_time(entity this)
Definition: client.qc:1315
void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage)
Definition: weaponstats.qc:101
string autocvar_sv_defaultplayermodel_red
Definition: player.qh:16
bool accuracy_isgooddamage(entity attacker, entity targ)
Definition: accuracy.qc:112
vector v_right
Definition: csprogsdefs.qc:31
float anim_implicit_time
Definition: animdecide.qh:117
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
const int ANIMSTATE_DEAD2
Definition: animdecide.qh:126
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
void Drop_Special_Items(entity player)
Definition: player.qc:42
float clientcolors
float frame
primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
Definition: anim.qh:6
entity weaponentities[MAX_WEAPONSLOTS]
Definition: weapon.qh:14
float teleportable
Definition: teleporters.qh:24
setorigin(ent, v)
float dphitcontentsmask
float pauseregen_finished
Definition: client.qh:342
#define setthink(e, f)
const int ANIMSTATE_DEAD1
Definition: animdecide.qh:125
void SUB_SetFade(entity ent, float vanish_time, float fading_time)
Definition: subs.qc:77
float autocvar_sv_gibhealth
Definition: player.qh:19
vector angles
Definition: csprogsdefs.qc:104
void Drag_MoveDrag(entity from, entity to)
Definition: cheats.qc:1000
float default_player_alpha
Definition: world.qh:66
float anim_upper_implicit_action
Definition: animdecide.qh:120
#define DEATH_ISSPECIAL(t)
Definition: all.qh:35
#define sound(e, c, s, v, a)
Definition: sound.qh:52
void CopyBody(entity this, float keepvelocity)
Definition: player.qc:64
vector glowmod
float autocvar_g_balance_armor_blockpercent
Definition: damage.qh:21
void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: player.qc:178
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition: weapon.qh:41
float time
Definition: csprogsdefs.qc:16
const int ANIMSTATE_DUCK
Definition: animdecide.qh:127
vector velocity
Definition: csprogsdefs.qc:103
ERASEABLE bool fexists(string f)
Definition: file.qh:4
int m_id
Definition: weapon.qh:42
float death_time
#define makevectors
Definition: post.qh:21
#define FOREACH(list, cond, body)
Definition: iter.qh:19
PRECACHE(PlayerModels)
Definition: player.qc:669
void precache_all_playermodels(string pattern)
Definition: player.qc:651
float DAMAGE_NO
Definition: progsdefs.qc:282
string autocvar_sv_defaultplayermodel_pink
Definition: player.qh:15
float anim_time
Definition: animdecide.qh:109
void set_movetype(entity this, int mt)
#define IS_PLAYER(v)
Definition: utils.qh:9
void CopyBody_Think(entity this)
Definition: player.qc:50
var void func_null()
float frame1time
start time of framegroup animation
Definition: anim.qh:22
bool autocvar_sv_precacheplayermodels
Definition: player.qh:5
float solid
Definition: csprogsdefs.qc:99
const float SOLID_CORPSE
Definition: csprogsdefs.qc:249