Xonotic
client.qc
Go to the documentation of this file.
1 #include "client.qh"
2 
5 #include <common/effects/all.qh>
7 #include <common/ent_cs.qh>
10 #include <common/items/_mod.qh>
28 #include <common/net_linked.qh>
29 #include <common/net_notice.qh>
31 #include <common/physics/player.qh>
32 #include <common/playerstats.qh>
34 #include <common/state.qh>
35 #include <common/stats.qh>
36 #include <common/vehicles/all.qh>
38 #include <common/viewloc.qh>
39 #include <common/weapons/_all.qh>
41 #include <common/wepent.qh>
43 #include <lib/warpzone/common.qh>
44 #include <lib/warpzone/server.qh>
45 #include <server/anticheat.qh>
46 #include <server/antilag.qh>
47 #include <server/bot/api.qh>
49 #include <server/campaign.qh>
50 #include <server/chat.qh>
51 #include <server/cheats.qh>
52 #include <server/clientkill.qh>
53 #include <server/command/common.qh>
54 #include <server/command/common.qh>
55 #include <server/command/vote.qh>
56 #include <server/compat/quake3.qh>
57 #include <server/damage.qh>
58 #include <server/gamelog.qh>
59 #include <server/handicap.qh>
60 #include <server/hook.qh>
61 #include <server/impulse.qh>
62 #include <server/intermission.qh>
63 #include <server/ipban.qh>
64 #include <server/main.qh>
65 #include <server/mutators/_mod.qh>
66 #include <server/player.qh>
67 #include <server/portals.qh>
68 #include <server/race.qh>
69 #include <server/scores.qh>
70 #include <server/scores_rules.qh>
71 #include <server/spawnpoints.qh>
72 #include <server/teamplay.qh>
74 #include <server/weapons/common.qh>
79 #include <server/world.qh>
80 
81 STATIC_METHOD(Client, Add, void(Client this, int _team))
82 {
83  ClientConnect(this);
84  TRANSMUTE(Player, this);
85  this.frame = 12; // 7
86  this.team = _team;
87  PutClientInServer(this);
88 }
89 
90 STATIC_METHOD(Client, Remove, void(Client this))
91 {
92  TRANSMUTE(Observer, this);
93  PutClientInServer(this);
94  ClientDisconnect(this);
95 }
96 
98  WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
99 }
100 
102 {
103  if(!player) { return 0; } // not sure how, but best to be safe
104 
105  int spec_count = 0;
106 
107  FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
108  {
109  spec_count++;
110  });
111 
112  return spec_count;
113 }
114 
116 {
117  if(!player) { return; } // not sure how, but best to be safe
118 
119  int spec_count = 0;
120  FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
121  {
122  if(spec_count >= MAX_SPECTATORS)
123  break;
124  WriteByte(MSG_ENTITY, num_for_edict(it));
125  ++spec_count;
126  });
127 }
128 
129 bool ClientData_Send(entity this, entity to, int sf)
130 {
131  assert(to == this.owner, return false);
132 
133  entity e = to;
134  if (IS_SPEC(e)) e = e.enemy;
135 
136  sf = 0;
137  if (CS(e).race_completed) sf |= BIT(0); // forced scoreboard
138  if (CS(to).spectatee_status) sf |= BIT(1); // spectator ent number follows
139  if (CS(e).zoomstate) sf |= BIT(2); // zoomed
140  if (autocvar_sv_showspectators) sf |= BIT(4); // show spectators
141 
142  WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
143  WriteByte(MSG_ENTITY, sf);
144 
145  if (sf & BIT(1))
146  WriteByte(MSG_ENTITY, CS(to).spectatee_status);
147 
148  if(sf & BIT(4))
149  {
150  float specs = CountSpectators(e, to);
151  WriteByte(MSG_ENTITY, specs);
152  WriteSpectators(e, to);
153  }
154 
155  return true;
156 }
157 
159 {
160  Net_LinkEntity(CS(this).clientdata = new_pure(clientdata), false, 0, ClientData_Send);
161  CS(this).clientdata.drawonlytoclient = this;
162  CS(this).clientdata.owner = this;
163 }
164 
166 {
167  delete(CS(this).clientdata);
168  CS(this).clientdata = NULL;
169 }
170 
172 {
173  entity cd = CS(e).clientdata;
174  if (cd) { cd.SendFlags = 1; }
175 
176  // make it spectatable
177  FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e,
178  {
179  entity cd = CS(it).clientdata;
180  if (cd) { cd.SendFlags = 1; }
181  });
182 }
183 
184 
185 /*
186 =============
187 CheckPlayerModel
188 
189 Checks if the argument string can be a valid playermodel.
190 Returns a valid one in doubt.
191 =============
192 */
194 string CheckPlayerModel(string plyermodel) {
195  if(FallbackPlayerModel != cvar_defstring("_cl_playermodel"))
196  {
197  // note: we cannot summon Don Strunzone here, some player may
198  // still have the model string set. In case anyone manages how
199  // to change a cvar default, we'll have a small leak here.
200  FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
201  }
202  // only in right path
203  if(substring(plyermodel, 0, 14) != "models/player/")
204  return FallbackPlayerModel;
205  // only good file extensions
206  if(substring(plyermodel, -4, 4) != ".iqm"
207  && substring(plyermodel, -4, 4) != ".zym"
208  && substring(plyermodel, -4, 4) != ".dpm"
209  && substring(plyermodel, -4, 4) != ".md3"
210  && substring(plyermodel, -4, 4) != ".psk")
211  {
212  return FallbackPlayerModel;
213  }
214  // forbid the LOD models
215  if(substring(plyermodel, -9, 5) == "_lod1" || substring(plyermodel, -9, 5) == "_lod2")
216  return FallbackPlayerModel;
217  if(plyermodel != strtolower(plyermodel))
218  return FallbackPlayerModel;
219  // also, restrict to server models
221  {
222  if(!fexists(plyermodel))
223  return FallbackPlayerModel;
224  }
225  return plyermodel;
226 }
227 
228 void setplayermodel(entity e, string modelname)
229 {
230  precache_model(modelname);
231  _setmodel(e, modelname);
233  if(!autocvar_g_debug_globalsounds)
235 }
236 
238 void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
239 {
240  bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this, is_forced);
241  bool recount_ready = false;
242  PlayerState_detach(this);
243 
244  if (IS_PLAYER(this))
245  {
246  if(GetResource(this, RES_HEALTH) >= 1)
247  {
248  // despawn effect
249  Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
250  }
251 
252  // was a player, recount votes and ready status
253  if(IS_REAL_CLIENT(this))
254  {
255  if (vote_called) { VoteCount(false); }
256  this.ready = false;
257  recount_ready = true;
258  }
259  entcs_update_players(this);
260  }
261 
262  if (use_spawnpoint)
263  {
264  entity spot = SelectSpawnPoint(this, true);
265  if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
266  this.angles = vec2(spot.angles);
267  // offset it so that the spectator spawns higher off the ground, looks better this way
268  setorigin(this, spot.origin + STAT(PL_VIEW_OFS, this));
269  }
270  else // change origin to restore previous view origin
271  setorigin(this, this.origin + STAT(PL_VIEW_OFS, this) - STAT(PL_CROUCH_VIEW_OFS, this));
272  this.fixangle = true;
273 
274  if (IS_REAL_CLIENT(this))
275  {
276  msg_entity = this;
278  WriteEntity(MSG_ONE, this);
279  }
280  // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
281  // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
282  if(!autocvar_g_debug_globalsounds)
283  {
284  // needed for player sounds
285  this.model = "";
286  FixPlayermodel(this);
287  }
288  setmodel(this, MDL_Null);
289  setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
290  this.view_ofs = '0 0 0';
291 
292  RemoveGrapplingHooks(this);
293  Portal_ClearAll(this);
294  Unfreeze(this, false);
295  SetSpectatee(this, NULL);
296 
297  if (this.alivetime)
298  {
299  if (!warmup_stage)
300  PlayerStats_GameReport_Event_Player(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
301  this.alivetime = 0;
302  }
303 
304  if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
305 
306  TRANSMUTE(Observer, this);
307 
308  if(recount_ready) ReadyCount();
309 
310  WaypointSprite_PlayerDead(this);
311  accuracy_resend(this);
312 
313  if (CS(this).killcount != FRAGS_SPECTATOR && !game_stopped && CHAT_NOSPECTATORS())
314  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
315 
316  CS(this).spectatortime = time;
317  if(this.bot_attack)
318  IL_REMOVE(g_bot_targets, this);
319  this.bot_attack = false;
320  if(this.monster_attack)
322  this.monster_attack = false;
323  STAT(HUD, this) = HUD_NORMAL;
324  this.iscreature = false;
326  if(this.damagedbycontents)
328  this.damagedbycontents = false;
330  SetSpectatee_status(this, etof(this));
331  this.takedamage = DAMAGE_NO;
332  this.solid = SOLID_NOT;
333  set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink
334  this.flags = FL_CLIENT | FL_NOTARGET;
335  this.effects = 0;
336  SetResourceExplicit(this, RES_ARMOR, autocvar_g_balance_armor_start); // was 666?!
337  this.pauserotarmor_finished = 0;
338  this.pauserothealth_finished = 0;
339  this.pauseregen_finished = 0;
340  this.damageforcescale = 0;
341  this.death_time = 0;
342  this.respawn_flags = 0;
343  this.respawn_time = 0;
344  STAT(RESPAWN_TIME, this) = 0;
345  this.alpha = 0;
346  this.scale = 0;
347  this.fade_time = 0;
348  this.pain_finished = 0;
349  STAT(AIR_FINISHED, this) = 0;
350  //this.dphitcontentsmask = 0;
354  this.pushltime = 0;
355  this.istypefrag = 0;
356  setthink(this, func_null);
357  this.nextthink = 0;
358  this.deadflag = DEAD_NO;
359  UNSET_DUCKED(this);
360  STAT(REVIVE_PROGRESS, this) = 0;
361  this.revival_time = 0;
362  this.draggable = drag_undraggable;
363 
365  this.items = 0;
366  STAT(WEAPONS, this) = '0 0 0';
367  this.drawonlytoclient = this;
368 
369  this.viewloc = NULL;
370 
371  //this.spawnpoint_targ = NULL; // keep it so they can return to where they were?
372 
373  this.weaponmodel = "";
374  for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
375  {
376  this.weaponentities[slot] = NULL;
377  }
378  this.exteriorweaponentity = NULL;
379  CS(this).killcount = FRAGS_SPECTATOR;
380  this.velocity = '0 0 0';
381  this.avelocity = '0 0 0';
382  this.punchangle = '0 0 0';
383  this.punchvector = '0 0 0';
384  this.oldvelocity = this.velocity;
385  this.event_damage = func_null;
386  this.event_heal = func_null;
387 
388  for(int slot = 0; slot < MAX_AXH; ++slot)
389  {
390  entity axh = this.(AuxiliaryXhair[slot]);
391  this.(AuxiliaryXhair[slot]) = NULL;
392 
393  if(axh.owner == this && axh != NULL && !wasfreed(axh))
394  delete(axh);
395  }
396 
397  if (mutator_returnvalue)
398  {
399  // mutator prevents resetting teams+score
400  }
401  else
402  {
404  this.frags = FRAGS_SPECTATOR;
405  }
406 
408 
409  if (CS(this).just_joined)
410  CS(this).just_joined = false;
411 }
412 
414 {
415  get_model_parameters(this.model, this.skin);
418  if (s < 0) return SPECIES_HUMAN;
419  return s;
420 }
421 
423 void FixPlayermodel(entity player)
424 {
425  string defaultmodel = "";
426  int defaultskin = 0;
428  {
429  if(teamplay)
430  {
431  switch(player.team)
432  {
433  case NUM_TEAM_1: defaultmodel = autocvar_sv_defaultplayermodel_red; defaultskin = autocvar_sv_defaultplayerskin_red; break;
434  case NUM_TEAM_2: defaultmodel = autocvar_sv_defaultplayermodel_blue; defaultskin = autocvar_sv_defaultplayerskin_blue; break;
436  case NUM_TEAM_4: defaultmodel = autocvar_sv_defaultplayermodel_pink; defaultskin = autocvar_sv_defaultplayerskin_pink; break;
437  }
438  }
439 
440  if(defaultmodel == "")
441  {
442  defaultmodel = autocvar_sv_defaultplayermodel;
443  defaultskin = autocvar_sv_defaultplayerskin;
444  }
445 
446  int n = tokenize_console(defaultmodel);
447  if(n > 0)
448  {
449  defaultmodel = argv(floor(n * CS(player).model_randomizer));
450  // However, do NOT randomize if the player-selected model is in the list.
451  for (int i = 0; i < n; ++i)
452  if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin))
453  defaultmodel = argv(i);
454  }
455 
456  int i = strstrofs(defaultmodel, ":", 0);
457  if(i >= 0)
458  {
459  defaultskin = stof(substring(defaultmodel, i+1, -1));
460  defaultmodel = substring(defaultmodel, 0, i);
461  }
462  }
463  if(autocvar_sv_defaultcharacterskin && !defaultskin)
464  {
465  if(teamplay)
466  {
467  switch(player.team)
468  {
469  case NUM_TEAM_1: defaultskin = autocvar_sv_defaultplayerskin_red; break;
470  case NUM_TEAM_2: defaultskin = autocvar_sv_defaultplayerskin_blue; break;
471  case NUM_TEAM_3: defaultskin = autocvar_sv_defaultplayerskin_yellow; break;
472  case NUM_TEAM_4: defaultskin = autocvar_sv_defaultplayerskin_pink; break;
473  }
474  }
475 
476  if(!defaultskin)
477  defaultskin = autocvar_sv_defaultplayerskin;
478  }
479 
480  MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player);
481  defaultmodel = M_ARGV(0, string);
482  defaultskin = M_ARGV(1, int);
483 
484  bool chmdl = false;
485  int oldskin;
486  if(defaultmodel != "")
487  {
488  if (defaultmodel != player.model)
489  {
490  vector m1 = player.mins;
491  vector m2 = player.maxs;
492  setplayermodel (player, defaultmodel);
493  setsize (player, m1, m2);
494  chmdl = true;
495  }
496 
497  oldskin = player.skin;
498  player.skin = defaultskin;
499  } else {
500  if (player.playermodel != player.model || player.playermodel == "")
501  {
502  player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop
503  vector m1 = player.mins;
504  vector m2 = player.maxs;
505  setplayermodel (player, player.playermodel);
506  setsize (player, m1, m2);
507  chmdl = true;
508  }
509 
511  {
512  oldskin = player.skin;
513  player.skin = stof(player.playerskin);
514  }
515  else
516  {
517  oldskin = player.skin;
518  player.skin = defaultskin;
519  }
520  }
521 
522  if(chmdl || oldskin != player.skin) // model or skin has changed
523  {
524  player.species = player_getspecies(player); // update species
525  if(!autocvar_g_debug_globalsounds)
526  UpdatePlayerSounds(player); // update skin sounds
527  }
528 
529  if(!teamplay)
531  if(player.clientcolors != stof(autocvar_sv_defaultplayercolors))
533 }
534 
536 {
537  if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
538 
539  PlayerState_attach(this);
540  accuracy_resend(this);
541 
542  if (this.team < 0)
544 
545  entity spot = SelectSpawnPoint(this, false);
546  if (!spot) {
547  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
548  return; // spawn failed
549  }
550 
551  TRANSMUTE(Player, this);
552 
553  CS(this).wasplayer = true;
554  this.iscreature = true;
556  if(!this.damagedbycontents)
558  this.damagedbycontents = true;
560  this.solid = SOLID_SLIDEBOX;
566  this.frags = FRAGS_PLAYER;
568  this.flags = FL_CLIENT | FL_PICKUPITEMS;
569  if (autocvar__notarget)
570  this.flags |= FL_NOTARGET;
571  this.takedamage = DAMAGE_AIM;
573 
574  if (warmup_stage) {
575  SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
576  SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
577  SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
578  SetResource(this, RES_CELLS, warmup_start_ammo_cells);
579  SetResource(this, RES_PLASMA, warmup_start_ammo_plasma);
580  SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
582  SetResource(this, RES_ARMOR, warmup_start_armorvalue);
583  STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
584  } else {
585  SetResource(this, RES_SHELLS, start_ammo_shells);
586  SetResource(this, RES_BULLETS, start_ammo_nails);
587  SetResource(this, RES_ROCKETS, start_ammo_rockets);
588  SetResource(this, RES_CELLS, start_ammo_cells);
589  SetResource(this, RES_PLASMA, start_ammo_plasma);
590  SetResource(this, RES_FUEL, start_ammo_fuel);
592  SetResource(this, RES_ARMOR, start_armorvalue);
593  STAT(WEAPONS, this) = start_weapons;
594  if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
595  {
598  }
599  }
600  SetSpectatee_status(this, 0);
601 
602  PS(this).dual_weapons = '0 0 0';
603 
604  if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
605  StatusEffects_apply(STATUSEFFECT_Superweapons, this, time + autocvar_g_balance_superweapons_time, 0);
606 
607  this.items = start_items;
608 
609  float shieldtime = time + autocvar_g_spawnshieldtime;
610 
615  if (!sv_ready_restart_after_countdown && time < game_starttime)
616  {
617  float f = game_starttime - time;
618  shieldtime += f;
619  this.pauserotarmor_finished += f;
620  this.pauserothealth_finished += f;
621  this.pauseregen_finished += f;
622  }
623 
624  StatusEffects_apply(STATUSEFFECT_SpawnShield, this, shieldtime, 0);
625 
627  this.death_time = 0;
628  this.respawn_flags = 0;
629  this.respawn_time = 0;
630  STAT(RESPAWN_TIME, this) = 0;
632  this.fade_time = 0;
633  this.pain_finished = 0;
634  this.pushltime = 0;
635  setthink(this, func_null); // players have no think function
636  this.nextthink = 0;
637  this.dmg_team = 0;
638  PS(this).ballistics_density = autocvar_g_ballistics_density_player;
639 
640  this.deadflag = DEAD_NO;
641 
642  this.angles = spot.angles;
643  this.angles_z = 0; // never spawn tilted even if the spot says to
644  if (IS_BOT_CLIENT(this))
645  {
646  this.v_angle = this.angles;
647  bot_aim_reset(this);
648  }
649  this.fixangle = true; // turn this way immediately
650  this.oldvelocity = this.velocity = '0 0 0';
651  this.avelocity = '0 0 0';
652  this.punchangle = '0 0 0';
653  this.punchvector = '0 0 0';
654 
655  STAT(REVIVE_PROGRESS, this) = 0;
656  this.revival_time = 0;
657 
658  STAT(AIR_FINISHED, this) = 0;
660  this.watertype = CONTENT_EMPTY;
661 
662  entity spawnevent = new_pure(spawnevent);
663  spawnevent.owner = this;
664  Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
665 
666  // Cut off any still running player sounds.
667  stopsound(this, CH_PLAYER_SINGLE);
668 
669  this.model = "";
670  FixPlayermodel(this);
671  this.drawonlytoclient = NULL;
672 
673  this.viewloc = NULL;
674 
675  for(int slot = 0; slot < MAX_AXH; ++slot)
676  {
677  entity axh = this.(AuxiliaryXhair[slot]);
678  this.(AuxiliaryXhair[slot]) = NULL;
679 
680  if(axh.owner == this && axh != NULL && !wasfreed(axh))
681  delete(axh);
682  }
683 
684  this.spawnpoint_targ = NULL;
685 
686  UNSET_DUCKED(this);
687  this.view_ofs = STAT(PL_VIEW_OFS, this);
688  setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
689  this.spawnorigin = spot.origin;
690  setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
691  // don't reset back to last position, even if new position is stuck in solid
692  this.oldorigin = this.origin;
693  if(this.conveyor)
694  IL_REMOVE(g_conveyed, this);
695  this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
696  if(this.swampslug)
697  IL_REMOVE(g_swamped, this);
698  this.swampslug = NULL;
699  this.swamp_interval = 0;
700  if(this.ladder_entity)
701  IL_REMOVE(g_ladderents, this);
702  this.ladder_entity = NULL;
703  IL_EACH(g_counters, it.realowner == this,
704  {
705  delete(it);
706  });
707  STAT(HUD, this) = HUD_NORMAL;
708 
709  this.event_damage = PlayerDamage;
710  this.event_heal = PlayerHeal;
711 
712  this.draggable = func_null;
713 
714  if(!this.bot_attack)
715  IL_PUSH(g_bot_targets, this);
716  this.bot_attack = true;
717  if(!this.monster_attack)
718  IL_PUSH(g_monster_targets, this);
719  this.monster_attack = true;
720  navigation_dynamicgoal_init(this, false);
721 
723 
724  // player was spectator
725  if (CS(this).killcount == FRAGS_SPECTATOR) {
726  PlayerScore_Clear(this);
727  CS(this).killcount = 0;
728  CS(this).startplaytime = time;
729  }
730 
731  for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
732  {
733  .entity weaponentity = weaponentities[slot];
734  CL_SpawnWeaponentity(this, weaponentity);
735  }
737  this.colormod = '1 1 1' * autocvar_g_player_brightness;
738  this.exteriorweaponentity.alpha = default_weapon_alpha;
739 
740  this.speedrunning = false;
741 
742  this.counter_cnt = 0;
743  this.fragsfilter_cnt = 0;
744 
745  target_voicescript_clear(this);
746 
747  // reset fields the weapons may use
748  FOREACH(Weapons, true, {
749  it.wr_resetplayer(it, this);
750  // reload all reloadable weapons
751  if (it.spawnflags & WEP_FLAG_RELOADABLE) {
752  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
753  {
754  .entity weaponentity = weaponentities[slot];
755  this.(weaponentity).weapon_load[it.m_id] = it.reloading_ammo;
756  }
757  }
758  });
759 
760  Unfreeze(this, false);
761 
762  MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
763 
764  {
765  string s = spot.target;
766  if(g_assault || g_race) // TODO: make targeting work in assault & race without this hack
767  spot.target = string_null;
768  SUB_UseTargets(spot, this, NULL);
769  if(g_assault || g_race)
770  spot.target = s;
771  }
772 
774  {
775  sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n"));
776  delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
777  }
778 
779  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
780  {
781  .entity weaponentity = weaponentities[slot];
782  entity w_ent = this.(weaponentity);
783  if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
784  w_ent.m_switchweapon = w_getbestweapon(this, weaponentity);
785  else
786  w_ent.m_switchweapon = WEP_Null;
787  w_ent.m_weapon = WEP_Null;
788  w_ent.weaponname = "";
789  w_ent.m_switchingweapon = WEP_Null;
790  w_ent.cnt = -1;
791  }
792 
793  MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
794 
795  if (CS(this).impulse) ImpulseCommands(this);
796 
797  W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
798  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
799  {
800  .entity weaponentity = weaponentities[slot];
801  W_WeaponFrame(this, weaponentity);
802  }
803 
804  if (!warmup_stage && !this.alivetime)
805  this.alivetime = time;
806 
807  antilag_clear(this, CS(this));
808 }
809 
812 {
813  if (IS_BOT_CLIENT(this)) {
814  TRANSMUTE(Player, this);
815  } else if (IS_REAL_CLIENT(this)) {
816  msg_entity = this;
818  WriteEntity(MSG_ONE, this);
819  }
820  if (game_stopped)
821  TRANSMUTE(Observer, this);
822 
823  bool use_spawnpoint = (!this.enemy); // check this.enemy here since SetSpectatee will clear it
824  SetSpectatee(this, NULL);
825 
826  // reset player keys
827  if(PS(this))
828  PS(this).itemkeys = 0;
829 
831 
832  if (IS_OBSERVER(this)) {
833  PutObserverInServer(this, false, use_spawnpoint);
834  } else if (IS_PLAYER(this)) {
835  PutPlayerInServer(this);
836  }
837 
839 }
840 
841 // TODO do we need all these fields, or should we stop autodetecting runtime
842 // changes and just have a console command to update this?
844 {
845  WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
846  return = true;
847  msg_entity = to;
848  // MSG_INIT replacement
849  // TODO: make easier to use
851  W_PROP_reload(MSG_ONE, to);
852  ClientInit_misc(this);
853  MUTATOR_CALLHOOK(Ent_Init);
854 }
856 {
857  int channel = MSG_ONE;
858  WriteHeader(channel, ENT_CLIENT_INIT);
859  WriteByte(channel, g_nexball_meter_period * 32);
860  WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
861  WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
862  WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
863  WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
864  WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
865  WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
866  WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
867  WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
868 
869  if(autocvar_sv_foginterval && world.fog != "")
870  WriteString(channel, world.fog);
871  else
872  WriteString(channel, "");
873  WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
874  WriteByte(channel, this.cnt * 255.0); // g_balance_damagepush_speedfactor
875  WriteByte(channel, serverflags);
877 }
878 
880 {
881  this.nextthink = time;
883  {
885  this.SendFlags |= 1;
886  }
888  {
890  this.SendFlags |= 1;
891  }
892 }
893 
895 {
896  entity e = new_pure(clientinit);
898  Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
899 
901 }
902 
903 /*
904 =============
905 SetNewParms
906 =============
907 */
908 void SetNewParms ()
909 {
910  // initialize parms for a new player
911  parm1 = -(86400 * 366);
912 
914 }
915 
916 /*
917 =============
918 SetChangeParms
919 =============
920 */
922 {
923  // save parms for level change
924  parm1 = CS(this).parm_idlesince - time;
925 
927 }
928 
929 /*
930 =============
931 DecodeLevelParms
932 =============
933 */
935 {
936  // load parms
937  CS(this).parm_idlesince = parm1;
938  if (CS(this).parm_idlesince == -(86400 * 366))
939  CS(this).parm_idlesince = time;
940 
941  // whatever happens, allow 60 seconds of idling directly after connect for map loading
942  CS(this).parm_idlesince = max(CS(this).parm_idlesince, time - autocvar_sv_maxidle + 60);
943 
945 }
946 
948 {
949  // send prediction settings to the client
950  if(autocvar_g_antilag == 3) // client side hitscan
951  stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
952  if(autocvar_sv_gentle)
953  stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
954 
955  stuffcmd(e, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
956  stuffcmd(e, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
957 
958  stuffcmd(e, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin));
959 
961 }
962 
963 bool findinlist_abbrev(string tofind, string list)
964 {
965  if(list == "" || tofind == "")
966  return false; // empty list or search, just return
967 
968  // this function allows abbreviated strings!
969  FOREACH_WORD(list, it == substring(tofind, 0, strlen(it)),
970  {
971  return true;
972  });
973 
974  return false;
975 }
976 
977 bool PlayerInIPList(entity p, string iplist)
978 {
979  // some safety checks (never allow local?)
980  if(p.netaddress == "local" || p.netaddress == "" || !IS_REAL_CLIENT(p))
981  return false;
982 
983  return findinlist_abbrev(p.netaddress, iplist);
984 }
985 
986 bool PlayerInIDList(entity p, string idlist)
987 {
988  // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this
989  if(!p.crypto_idfp)
990  return false;
991 
992  return findinlist_abbrev(p.crypto_idfp, idlist);
993 }
994 
995 bool PlayerInList(entity player, string list)
996 {
997  return boolean(PlayerInIDList(player, list) || PlayerInIPList(player, list));
998 }
999 
1000 #ifdef DP_EXT_PRECONNECT
1001 /*
1002 =============
1003 ClientPreConnect
1004 
1005 Called once (not at each match start) when a client begins a connection to the server
1006 =============
1007 */
1008 void ClientPreConnect(entity this)
1009 {
1011  {
1012  GameLogEcho(sprintf(":connect:%d:%d:%s",
1013  this.playerid,
1014  etof(this),
1015  ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot")
1016  ));
1017  }
1018 }
1019 #endif
1020 
1022 {
1023  if (CS(this).version_mismatch) {
1024  if(CS(this).version < autocvar_gameversion) {
1025  return strcat("This is Xonotic ", autocvar_g_xonoticversion,
1026  "\n^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8");
1027  } else {
1028  return strcat("This is Xonotic ", autocvar_g_xonoticversion,
1029  "\n^3This server is using an outdated Xonotic version.\n\n\n ### THIS SERVER IS INCOMPATIBLE AND THUS YOU CANNOT JOIN ###.^8");
1030  }
1031  } else {
1032  return strcat("Welcome to Xonotic ", autocvar_g_xonoticversion);
1033  }
1034 }
1035 
1037 {
1038  MUTATOR_CALLHOOK(BuildMutatorsPrettyString, "");
1039  string modifications = M_ARGV(0, string);
1040 
1041  if(g_weaponarena)
1042  {
1044  modifications = strcat(modifications, ", ", ftos(g_weaponarena_random), " of ", g_weaponarena_list, " Arena");
1045  else
1046  modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
1047  }
1048  else if(cvar("g_balance_blaster_weaponstartoverride") == 0)
1049  modifications = strcat(modifications, ", No start weapons");
1050  if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
1051  modifications = strcat(modifications, ", Low gravity");
1052  if(g_weapon_stay && !g_cts)
1053  modifications = strcat(modifications, ", Weapons stay");
1054  if(autocvar_g_jetpack)
1055  modifications = strcat(modifications, ", Jet pack");
1056  modifications = substring(modifications, 2, strlen(modifications) - 2);
1057 
1058  string versionmessage = GetClientVersionMessage(this);
1059  string s = strcat(versionmessage, "^8\n^8\nserver is ^9", autocvar_hostname, "^8\n");
1060 
1061  s = strcat(s, "^8\nmatch type is ^1", gamemode_name, "^8\n");
1062 
1063  if(modifications != "")
1064  s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n");
1065 
1067  {
1070  }
1071 
1072  if (cache_mutatormsg != "") {
1073  s = strcat(s, "\n\n^8special gameplay tips: ^7", cache_mutatormsg);
1074  }
1075 
1076  string mutator_msg = "";
1077  MUTATOR_CALLHOOK(BuildGameplayTipsString, mutator_msg);
1078  mutator_msg = M_ARGV(0, string);
1079 
1080  s = strcat(s, mutator_msg); // trust that the mutator will do proper formatting
1081 
1082  string motd = autocvar_sv_motd;
1083  if (motd != "") {
1084  s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd));
1085  }
1086  return s;
1087 }
1088 
1097 {
1098  if (Ban_MaybeEnforceBanOnce(this)) return;
1099  assert(!IS_CLIENT(this), return);
1100  this.flags |= FL_CLIENT;
1101  assert(player_count >= 0, player_count = 0);
1102 
1103  TRANSMUTE(Client, this);
1104  CS(this).version_nagtime = time + 10 + random() * 10;
1105 
1106  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname);
1107 
1108  bot_clientconnect(this);
1109 
1111 
1112  TRANSMUTE(Observer, this);
1113 
1114  PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid));
1115 
1116  // always track bots, don't ask for cl_allow_uidtracking
1117  if (IS_BOT_CLIENT(this))
1118  PlayerStats_GameReport_AddPlayer(this);
1119  else
1120  CS(this).allowed_timeouts = autocvar_sv_timeout_number;
1121 
1123  GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false)));
1124 
1125  CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects
1126 
1127  stuffcmd(this, clientstuff, "\n");
1128  stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
1129 
1130  FixClientCvars(this);
1131 
1132  // get version info from player
1133  stuffcmd(this, "cmd clientversion $gameversion\n");
1134 
1135  // notify about available teams
1136  if (teamplay)
1137  {
1138  entity balance = TeamBalance_CheckAllowedTeams(this);
1139  int t = TeamBalance_GetAllowedTeams(balance);
1140  TeamBalance_Destroy(balance);
1141  stuffcmd(this, sprintf("set _teams_available %d\n", t));
1142  }
1143  else
1144  {
1145  stuffcmd(this, "set _teams_available 0\n");
1146  }
1147 
1149 
1150  CS(this).spectatortime = time;
1151  if (blockSpectators)
1152  {
1153  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
1154  }
1155 
1156  CS(this).jointime = time;
1157 
1158  if (IS_REAL_CLIENT(this))
1159  {
1160  if (g_weaponarena_weapons == WEPSET(TUBA))
1161  stuffcmd(this, "cl_cmd settemp chase_active 1\n");
1162  }
1163 
1164  if (!autocvar_sv_foginterval && world.fog != "")
1165  stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
1166 
1168  if(!MUTATOR_CALLHOOK(HideTeamNagger, this))
1170 
1171  CSQCMODEL_AUTOINIT(this);
1172 
1173  CS(this).model_randomizer = random();
1174 
1175  if (IS_REAL_CLIENT(this))
1176  sv_notice_join(this);
1177 
1178  this.move_qcphysics = autocvar_sv_qcphysics;
1179 
1180  // update physics stats (players can spawn before physics runs)
1181  Physics_UpdateStats(this);
1182 
1183  IL_EACH(g_initforplayer, it.init_for_player, {
1184  it.init_for_player(it, this);
1185  });
1186 
1187  Handicap_Initialize(this);
1188 
1190 
1191  if (IS_REAL_CLIENT(this))
1192  {
1193  if (!autocvar_g_campaign && !IS_PLAYER(this))
1194  {
1195  CS(this).motd_actived_time = -1;
1196  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
1197  }
1198  }
1199 }
1200 /*
1201 =============
1202 ClientDisconnect
1203 
1204 Called when a client disconnects from the server
1205 =============
1206 */
1209 
1211 {
1212  assert(IS_CLIENT(this), return);
1213 
1214  PlayerStats_GameReport_FinalizePlayer(this);
1215  if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
1216  if (CS(this).active_minigame) part_minigame(this);
1217  if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
1218 
1220  GameLogEcho(strcat(":part:", ftos(this.playerid)));
1221 
1222  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
1223 
1224  if(IS_SPEC(this))
1225  SetSpectatee(this, NULL);
1226 
1228 
1229  strfree(CS(this).netname_previous); // needs to be before the CS entity is removed!
1231  ClientState_detach(this);
1232 
1233  Portal_ClearAll(this);
1234 
1235  Unfreeze(this, false);
1236 
1237  RemoveGrapplingHooks(this);
1238 
1239  // Here, everything has been done that requires this player to be a client.
1240 
1241  this.flags &= ~FL_CLIENT;
1242 
1243  if (this.chatbubbleentity) delete(this.chatbubbleentity);
1244  if (this.killindicator) delete(this.killindicator);
1245 
1246  IL_EACH(g_counters, it.realowner == this,
1247  {
1248  delete(it);
1249  });
1250 
1251  WaypointSprite_PlayerGone(this);
1252 
1254 
1255  strfree(this.clientstatus);
1256  if (this.personal) delete(this.personal);
1257 
1258  this.playerid = 0;
1259  ReadyCount();
1260  if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
1261 
1262  player_powerups_remove_all(this); // stop powerup sound
1263 
1264  ONREMOVE(this);
1265 }
1266 
1268 {
1269  this.nextthink = time;
1270  if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this)
1271  {
1272  if(this.owner) // but why can that ever be NULL?
1273  this.owner.chatbubbleentity = NULL;
1274  delete(this);
1275  return;
1276  }
1277 
1278  this.mdl = "";
1279 
1280  if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) )
1281  {
1282  if ( CS(this.owner).active_minigame && PHYS_INPUT_BUTTON_MINIGAME(this.owner) )
1283  this.mdl = "models/sprites/minigame_busy.iqm";
1284  else if (PHYS_INPUT_BUTTON_CHAT(this.owner))
1285  this.mdl = "models/misc/chatbubble.spr";
1286  }
1287 
1288  if ( this.model != this.mdl )
1289  _setmodel(this, this.mdl);
1290 
1291 }
1292 
1294 {
1295  if (this.alpha < 0)
1296  return;
1297  // spawn a chatbubble entity if needed
1298  if (!this.chatbubbleentity)
1299  {
1300  this.chatbubbleentity = new(chatbubbleentity);
1301  this.chatbubbleentity.owner = this;
1302  this.chatbubbleentity.exteriormodeltoclient = this;
1304  this.chatbubbleentity.nextthink = time;
1305  setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
1306  //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
1307  setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1');
1308  setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth
1309  this.chatbubbleentity.mdl = this.chatbubbleentity.model;
1310  //this.chatbubbleentity.model = "";
1311  this.chatbubbleentity.effects = EF_LOWPRECISION;
1312  }
1313 }
1314 
1316 {
1317  if(MUTATOR_CALLHOOK(CalculateRespawnTime, this))
1318  return;
1319 
1320  float gametype_setting_tmp;
1321  float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
1322  float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
1323  float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large);
1324  float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count);
1325  float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count);
1326  float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves);
1327 
1328  float pcount = 1; // Include myself whether or not team is already set right and I'm a "player".
1329  if (teamplay)
1330  {
1331  FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
1332  if(it.team == this.team)
1333  ++pcount;
1334  });
1335  if (sdelay_small_count == 0)
1336  sdelay_small_count = 1;
1337  if (sdelay_large_count == 0)
1338  sdelay_large_count = 1;
1339  }
1340  else
1341  {
1342  FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
1343  ++pcount;
1344  });
1345  if (sdelay_small_count == 0)
1346  {
1347  if (IS_INDEPENDENT_PLAYER(this))
1348  {
1349  // Players play independently. No point in requiring enemies.
1350  sdelay_small_count = 1;
1351  }
1352  else
1353  {
1354  // Players play AGAINST each other. Enemies required.
1355  sdelay_small_count = 2;
1356  }
1357  }
1358  if (sdelay_large_count == 0)
1359  {
1360  if (IS_INDEPENDENT_PLAYER(this))
1361  {
1362  // Players play independently. No point in requiring enemies.
1363  sdelay_large_count = 1;
1364  }
1365  else
1366  {
1367  // Players play AGAINST each other. Enemies required.
1368  sdelay_large_count = 2;
1369  }
1370  }
1371  }
1372 
1373  float sdelay;
1374 
1375  if (pcount <= sdelay_small_count)
1376  sdelay = sdelay_small;
1377  else if (pcount >= sdelay_large_count)
1378  sdelay = sdelay_large;
1379  else // NOTE: this case implies sdelay_large_count > sdelay_small_count.
1380  sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count);
1381 
1382  if(waves)
1383  this.respawn_time = ceil((time + sdelay) / waves) * waves;
1384  else
1385  this.respawn_time = time + sdelay;
1386 
1387  if(sdelay < sdelay_max)
1388  this.respawn_time_max = time + sdelay_max;
1389  else
1390  this.respawn_time_max = this.respawn_time;
1391 
1392  if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75))
1393  this.respawn_countdown = 10; // first number to count down from is 10
1394  else
1395  this.respawn_countdown = -1; // do not count down
1396 
1399 }
1400 
1401 // LordHavoc: this hack will be removed when proper _pants/_shirt layers are
1402 // added to the model skins
1403 /*void UpdateColorModHack()
1404 {
1405  float c;
1406  c = this.clientcolors & 15;
1407  // LordHavoc: only bothering to support white, green, red, yellow, blue
1408  if (!teamplay) this.colormod = '0 0 0';
1409  else if (c == 0) this.colormod = '1.00 1.00 1.00';
1410  else if (c == 3) this.colormod = '0.10 1.73 0.10';
1411  else if (c == 4) this.colormod = '1.73 0.10 0.10';
1412  else if (c == 12) this.colormod = '1.22 1.22 0.10';
1413  else if (c == 13) this.colormod = '0.10 0.10 1.73';
1414  else this.colormod = '1 1 1';
1415 }*/
1416 
1417 void respawn(entity this)
1418 {
1419  bool damagedbycontents_prev = this.damagedbycontents;
1420  if(this.alpha >= 0)
1421  {
1423  {
1424  this.solid = SOLID_NOT;
1425  this.takedamage = DAMAGE_NO;
1426  this.damagedbycontents = false;
1427  set_movetype(this, MOVETYPE_FLY);
1428  this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
1432  Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1);
1435  }
1436  else
1437  SUB_SetFade (this, time, 1); // fade out the corpse immediately
1438  }
1439 
1440  CopyBody(this, 1);
1441  this.damagedbycontents = damagedbycontents_prev;
1442 
1443  this.effects |= EF_NODRAW; // prevent another CopyBody
1444  PutClientInServer(this);
1445 }
1446 
1447 void play_countdown(entity this, float finished, Sound samp)
1448 {
1449  TC(Sound, samp);
1450  if(IS_REAL_CLIENT(this))
1451  if(floor(finished - time - frametime) != floor(finished - time))
1452  if(finished - time < 6)
1453  sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
1454 }
1455 
1456 // it removes special powerups not handled by StatusEffects
1458 {
1459  if (this.items & (IT_SUPERWEAPON | IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS))
1460  {
1461  // don't play the poweroff sound when the game restarts or the player disconnects
1462  if (time > game_starttime + 1 && IS_CLIENT(this))
1463  sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
1464  if (this.items & (IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS))
1465  stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
1466  this.items -= (this.items & (IT_SUPERWEAPON | IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS));
1467  }
1468 }
1469 
1471 {
1472  if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !game_stopped)
1473  this.modelflags |= MF_ROCKET;
1474  else
1475  this.modelflags &= ~MF_ROCKET;
1476 
1477  this.effects &= ~EF_NODEPTHTEST;
1478 
1479  if (IS_DEAD(this))
1481 
1482  if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
1483  return;
1484 
1485  // add a way to see what the items were BEFORE all of these checks for the mutator hook
1486  int items_prev = this.items;
1487 
1488  if (!MUTATOR_IS_ENABLED(mutator_instagib))
1489  {
1490  // NOTE: superweapons are a special case and as such are handled here instead of the status effects system
1491  if (this.items & IT_SUPERWEAPON)
1492  {
1493  if (!(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS))
1494  {
1495  StatusEffects_remove(STATUSEFFECT_Superweapons, this, STATUSEFFECT_REMOVE_NORMAL);
1496  this.items = this.items - (this.items & IT_SUPERWEAPON);
1497  //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
1498  Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
1499  }
1500  else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
1501  {
1502  // don't let them run out
1503  }
1504  else
1505  {
1506  play_countdown(this, StatusEffects_gettime(STATUSEFFECT_Superweapons, this), SND_POWEROFF);
1507  if (time > StatusEffects_gettime(STATUSEFFECT_Superweapons, this))
1508  {
1509  this.items = this.items - (this.items & IT_SUPERWEAPON);
1510  STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
1511  //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
1512  Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
1513  }
1514  }
1515  }
1516  else if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
1517  {
1518  if (time < StatusEffects_gettime(STATUSEFFECT_Superweapons, this) || (this.items & IT_UNLIMITED_SUPERWEAPONS))
1519  {
1520  this.items = this.items | IT_SUPERWEAPON;
1521  if(!(this.items & IT_UNLIMITED_SUPERWEAPONS))
1522  {
1523  if(!g_cts)
1524  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
1525  Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
1526  }
1527  }
1528  else
1529  {
1530  if(StatusEffects_active(STATUSEFFECT_Superweapons, this))
1531  StatusEffects_remove(STATUSEFFECT_Superweapons, this, STATUSEFFECT_REMOVE_TIMEOUT);
1532  STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
1533  }
1534  }
1535  else if(StatusEffects_active(STATUSEFFECT_Superweapons, this)) // cheaper to check than to update each frame!
1536  {
1537  StatusEffects_remove(STATUSEFFECT_Superweapons, this, STATUSEFFECT_REMOVE_CLEAR);
1538  }
1539  }
1540 
1542  this.effects = this.effects | EF_NODEPTHTEST;
1543 
1545  this.effects = this.effects | EF_FULLBRIGHT;
1546 
1547  MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
1548 }
1549 
1550 float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
1551 {
1552  if(current > stable)
1553  return current;
1554  else if(current > stable - 0.25) // when close enough, "snap"
1555  return stable;
1556  else
1557  return min(stable, current + (stable - current) * regenfactor * regenframetime);
1558 }
1559 
1560 float CalcRot(float current, float stable, float rotfactor, float rotframetime)
1561 {
1562  if(current < stable)
1563  return current;
1564  else if(current < stable + 0.25) // when close enough, "snap"
1565  return stable;
1566  else
1567  return max(stable, current + (stable - current) * rotfactor * rotframetime);
1568 }
1569 
1570 void RotRegen(entity this, Resource res, float limit_mod,
1571  float regenstable, float regenfactor, float regenlinear, float regenframetime,
1572  float rotstable, float rotfactor, float rotlinear, float rotframetime)
1573 {
1574  float old = GetResource(this, res);
1575  float current = old;
1576  if(current > rotstable)
1577  {
1578  if(rotframetime > 0)
1579  {
1580  current = CalcRot(current, rotstable, rotfactor, rotframetime);
1581  current = max(rotstable, current - rotlinear * rotframetime);
1582  }
1583  }
1584  else if(current < regenstable)
1585  {
1586  if(regenframetime > 0)
1587  {
1588  current = CalcRegen(current, regenstable, regenfactor, regenframetime);
1589  current = min(regenstable, current + regenlinear * regenframetime);
1590  }
1591  }
1592 
1593  float limit = GetResourceLimit(this, res) * limit_mod;
1594  if(current > limit)
1595  current = limit;
1596 
1597  if (current != old)
1598  SetResource(this, res, current);
1599 }
1600 
1602 {
1603  float max_mod, regen_mod, rot_mod, limit_mod;
1604  max_mod = regen_mod = rot_mod = limit_mod = 1;
1605 
1606  float regen_health = autocvar_g_balance_health_regen;
1607  float regen_health_linear = autocvar_g_balance_health_regenlinear;
1608  float regen_health_rot = autocvar_g_balance_health_rot;
1609  float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
1610  float regen_health_stable = autocvar_g_balance_health_regenstable;
1611  float regen_health_rotstable = autocvar_g_balance_health_rotstable;
1612  bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
1613  regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
1614  max_mod = M_ARGV(1, float);
1615  regen_mod = M_ARGV(2, float);
1616  rot_mod = M_ARGV(3, float);
1617  limit_mod = M_ARGV(4, float);
1618  regen_health = M_ARGV(5, float);
1619  regen_health_linear = M_ARGV(6, float);
1620  regen_health_rot = M_ARGV(7, float);
1621  regen_health_rotlinear = M_ARGV(8, float);
1622  regen_health_stable = M_ARGV(9, float);
1623  regen_health_rotstable = M_ARGV(10, float);
1624 
1625  float rotstable, regenstable, rotframetime, regenframetime;
1626 
1627  if(!mutator_returnvalue)
1628  if(!STAT(FROZEN, this))
1629  {
1632  regenframetime = (time > this.pauseregen_finished) ? (regen_mod * frametime) : 0;
1633  rotframetime = (time > this.pauserotarmor_finished) ? (rot_mod * frametime) : 0;
1634  RotRegen(this, RES_ARMOR, limit_mod,
1637 
1638  // NOTE: max_mod is only applied to health
1639  regenstable = regen_health_stable * max_mod;
1640  rotstable = regen_health_rotstable * max_mod;
1641  regenframetime = (time > this.pauseregen_finished) ? (regen_mod * frametime) : 0;
1642  rotframetime = (time > this.pauserothealth_finished) ? (rot_mod * frametime) : 0;
1643  RotRegen(this, RES_HEALTH, limit_mod,
1644  regenstable, regen_health, regen_health_linear, regenframetime,
1645  rotstable, regen_health_rot, regen_health_rotlinear, rotframetime);
1646  }
1647 
1648  // if player rotted to death... die!
1649  // check this outside above checks, as player may still be able to rot to death
1650  if(GetResource(this, RES_HEALTH) < 1)
1651  {
1652  if(this.vehicle)
1653  vehicles_exit(this.vehicle, VHEF_RELEASE);
1654  if(this.event_damage)
1655  this.event_damage(this, this, this, 1, DEATH_ROT.m_id, DMG_NOWEP, this.origin, '0 0 0');
1656  }
1657 
1658  if (!(this.items & IT_UNLIMITED_AMMO))
1659  {
1662  regenframetime = ((time > this.pauseregen_finished) && (this.items & ITEM_JetpackRegen.m_itemid)) ? frametime : 0;
1663  rotframetime = (time > this.pauserotfuel_finished) ? frametime : 0;
1664  RotRegen(this, RES_FUEL, 1,
1667  }
1668 }
1669 
1671 void SetZoomState(entity this, float newzoom)
1672 {
1673  if(newzoom != CS(this).zoomstate)
1674  {
1675  CS(this).zoomstate = newzoom;
1676  ClientData_Touch(this);
1677  }
1678  zoomstate_set = true;
1679 }
1680 
1682 {
1684  if (game_stopped)
1685  {
1686  CS(this).pressedkeys = 0;
1687  STAT(PRESSED_KEYS, this) = 0;
1688  return;
1689  }
1690 
1691  // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
1692  int keys = STAT(PRESSED_KEYS, this);
1693  keys = BITSET(keys, KEY_FORWARD, CS(this).movement.x > 0);
1694  keys = BITSET(keys, KEY_BACKWARD, CS(this).movement.x < 0);
1695  keys = BITSET(keys, KEY_RIGHT, CS(this).movement.y > 0);
1696  keys = BITSET(keys, KEY_LEFT, CS(this).movement.y < 0);
1697 
1698  keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this));
1699  keys = BITSET(keys, KEY_CROUCH, IS_DUCKED(this)); // workaround: player can't un-crouch until their path is clear, so we keep the button held here
1700  keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
1701  keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
1702  CS(this).pressedkeys = keys; // store for other users
1703 
1704  STAT(PRESSED_KEYS, this) = keys;
1705 }
1706 
1707 /*
1708 ======================
1709 spectate mode routines
1710 ======================
1711 */
1712 
1713 void SpectateCopy(entity this, entity spectatee)
1714 {
1715  TC(Client, this); TC(Client, spectatee);
1716 
1717  MUTATOR_CALLHOOK(SpectateCopy, spectatee, this);
1718  PS(this) = PS(spectatee);
1719  this.armortype = spectatee.armortype;
1720  SetResourceExplicit(this, RES_ARMOR, GetResource(spectatee, RES_ARMOR));
1721  SetResourceExplicit(this, RES_CELLS, GetResource(spectatee, RES_CELLS));
1722  SetResourceExplicit(this, RES_PLASMA, GetResource(spectatee, RES_PLASMA));
1723  SetResourceExplicit(this, RES_SHELLS, GetResource(spectatee, RES_SHELLS));
1724  SetResourceExplicit(this, RES_BULLETS, GetResource(spectatee, RES_BULLETS));
1725  SetResourceExplicit(this, RES_ROCKETS, GetResource(spectatee, RES_ROCKETS));
1726  SetResourceExplicit(this, RES_FUEL, GetResource(spectatee, RES_FUEL));
1727  this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
1729  CS(this).impulse = 0;
1730  this.disableclientprediction = 1; // no need to run prediction on a spectator
1731  this.items = spectatee.items;
1732  STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee);
1733  STAT(HIT_TIME, this) = STAT(HIT_TIME, spectatee);
1734  STAT(AIR_FINISHED, this) = STAT(AIR_FINISHED, spectatee);
1735  STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
1736  STAT(WEAPONS, this) = STAT(WEAPONS, spectatee);
1737  this.punchangle = spectatee.punchangle;
1738  this.view_ofs = spectatee.view_ofs;
1739  this.velocity = spectatee.velocity;
1740  this.dmg_take = spectatee.dmg_take;
1741  this.dmg_save = spectatee.dmg_save;
1742  this.dmg_inflictor = spectatee.dmg_inflictor;
1743  this.v_angle = spectatee.v_angle;
1744  this.angles = spectatee.v_angle;
1745  STAT(FROZEN, this) = STAT(FROZEN, spectatee);
1746  STAT(REVIVE_PROGRESS, this) = STAT(REVIVE_PROGRESS, spectatee);
1747  this.viewloc = spectatee.viewloc;
1748  if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
1749  this.fixangle = true;
1750  setorigin(this, spectatee.origin);
1751  setsize(this, spectatee.mins, spectatee.maxs);
1752  SetZoomState(this, CS(spectatee).zoomstate);
1753 
1754  anticheat_spectatecopy(this, spectatee);
1755  STAT(HUD, this) = STAT(HUD, spectatee);
1756  if(spectatee.vehicle)
1757  {
1758  this.angles = spectatee.v_angle;
1759 
1760  //this.fixangle = false;
1761  //this.velocity = spectatee.vehicle.velocity;
1762  this.vehicle_health = spectatee.vehicle_health;
1763  this.vehicle_shield = spectatee.vehicle_shield;
1764  this.vehicle_energy = spectatee.vehicle_energy;
1765  this.vehicle_ammo1 = spectatee.vehicle_ammo1;
1766  this.vehicle_ammo2 = spectatee.vehicle_ammo2;
1767  this.vehicle_reload1 = spectatee.vehicle_reload1;
1768  this.vehicle_reload2 = spectatee.vehicle_reload2;
1769 
1770  //msg_entity = this;
1771 
1772  // WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
1773  //WriteAngle(MSG_ONE, spectatee.v_angle.x);
1774  // WriteAngle(MSG_ONE, spectatee.v_angle.y);
1775  // WriteAngle(MSG_ONE, spectatee.v_angle.z);
1776 
1777  //WriteByte (MSG_ONE, SVC_SETVIEW);
1778  // WriteEntity(MSG_ONE, this);
1779  //makevectors(spectatee.v_angle);
1780  //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/
1781  }
1782 }
1783 
1785 {
1786  if(!this.enemy)
1787  return false;
1788 
1789  if(!IS_PLAYER(this.enemy) || this == this.enemy)
1790  {
1791  SetSpectatee(this, NULL);
1792  return false;
1793  }
1794 
1795  SpectateCopy(this, this.enemy);
1796 
1797  return true;
1798 }
1799 
1801 {
1802  if(!IS_PLAYER(this.enemy))
1803  return false;
1804 
1805  ClientData_Touch(this.enemy);
1806 
1807  msg_entity = this;
1809  WriteEntity(MSG_ONE, this.enemy);
1810  set_movetype(this, MOVETYPE_NONE);
1811  accuracy_resend(this);
1812 
1813  if(!SpectateUpdate(this))
1814  PutObserverInServer(this, false, true);
1815 
1816  return true;
1817 }
1818 
1819 void SetSpectatee_status(entity this, int spectatee_num)
1820 {
1821  int oldspectatee_status = CS(this).spectatee_status;
1822  CS(this).spectatee_status = spectatee_num;
1823 
1824  if (CS(this).spectatee_status != oldspectatee_status)
1825  {
1826  if (STAT(PRESSED_KEYS, this))
1827  {
1828  CS(this).pressedkeys = 0;
1829  STAT(PRESSED_KEYS, this) = 0;
1830  }
1831  ClientData_Touch(this);
1832  if (g_race || g_cts) race_InitSpectator();
1833  }
1834 }
1835 
1836 void SetSpectatee(entity this, entity spectatee)
1837 {
1838  if(IS_BOT_CLIENT(this))
1839  return; // bots abuse .enemy, this code is useless to them
1840 
1841  entity old_spectatee = this.enemy;
1842 
1843  this.enemy = spectatee;
1844 
1845  // WEAPONTODO
1846  // these are required to fix the spectator bug with arc
1847  if(old_spectatee)
1848  {
1849  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
1850  {
1851  .entity weaponentity = weaponentities[slot];
1852  if(old_spectatee.(weaponentity).arc_beam)
1853  old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS;
1854  }
1855  }
1856  if(spectatee)
1857  {
1858  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
1859  {
1860  .entity weaponentity = weaponentities[slot];
1861  if(spectatee.(weaponentity).arc_beam)
1862  spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS;
1863  }
1864  }
1865 
1866  if (spectatee)
1867  SetSpectatee_status(this, etof(spectatee));
1868 
1869  // needed to update spectator list
1870  if(old_spectatee) { ClientData_Touch(old_spectatee); }
1871 }
1872 
1873 bool Spectate(entity this, entity pl)
1874 {
1875  if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
1876  return false;
1877  pl = M_ARGV(1, entity);
1878 
1879  SetSpectatee(this, pl);
1880  return SpectateSet(this);
1881 }
1882 
1884 {
1885  entity ent = find(this.enemy, classname, STR_PLAYER);
1886 
1887  if (MUTATOR_CALLHOOK(SpectateNext, this, ent))
1888  ent = M_ARGV(1, entity);
1889  else if (!ent)
1890  ent = find(ent, classname, STR_PLAYER);
1891 
1892  if(ent) { SetSpectatee(this, ent); }
1893 
1894  return SpectateSet(this);
1895 }
1896 
1898 {
1899  // NOTE: chain order is from the highest to the lower entnum (unlike find)
1900  entity ent = findchain(classname, STR_PLAYER);
1901  if (!ent) // no player
1902  return false;
1903 
1904  entity first = ent;
1905  // skip players until current spectated player
1906  if(this.enemy)
1907  while(ent && ent != this.enemy)
1908  ent = ent.chain;
1909 
1910  switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first))
1911  {
1912  case MUT_SPECPREV_FOUND:
1913  ent = M_ARGV(1, entity);
1914  break;
1915  case MUT_SPECPREV_RETURN:
1916  return true;
1917  case MUT_SPECPREV_CONTINUE:
1918  default:
1919  {
1920  if(ent.chain)
1921  ent = ent.chain;
1922  else
1923  ent = first;
1924  break;
1925  }
1926  }
1927 
1928  SetSpectatee(this, ent);
1929  return SpectateSet(this);
1930 }
1931 
1932 /*
1933 =============
1934 ShowRespawnCountdown()
1935 
1936 Update a respawn countdown display.
1937 =============
1938 */
1940 {
1941  float number;
1942  if(!IS_DEAD(this)) // just respawned?
1943  return;
1944  else
1945  {
1946  number = ceil(this.respawn_time - time);
1947  if(number <= 0)
1948  return;
1949  if(number <= this.respawn_countdown)
1950  {
1951  this.respawn_countdown = number - 1;
1952  if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
1953  { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); }
1954  }
1955  }
1956 }
1957 
1960 {
1962  return false;
1963  stuffcmd(this, "menu_showteamselect\n");
1964  return true;
1965 }
1966 void Join(entity this)
1967 {
1968  TRANSMUTE(Player, this);
1969 
1970  if(!this.team_selected)
1973 
1975  campaign_bots_may_start = true;
1976 
1977  Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
1978 
1979  PutClientInServer(this);
1980 
1981  if(IS_PLAYER(this))
1982  if(teamplay && this.team != -1)
1983  {
1984  }
1985  else
1986  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
1987  this.team_selected = false;
1988 }
1989 
1991 {
1992  if(g_duel)
1993  return 2; // TODO: this workaround is needed since the mutator hook from duel can't be activated before the gametype is loaded (e.g. switching modes via gametype vote screen)
1994  int player_limit = autocvar_g_maxplayers;
1995  MUTATOR_CALLHOOK(GetPlayerLimit, player_limit);
1996  player_limit = M_ARGV(0, int);
1997  return player_limit;
1998 }
1999 
2006 int nJoinAllowed(entity this, entity ignore)
2007 {
2008  if(!ignore)
2009  // this is called that way when checking if anyone may be able to join (to build qcstatus)
2010  // so report 0 free slots if restricted
2011  {
2012  if(autocvar_g_forced_team_otherwise == "spectate")
2013  return 0;
2014  if(autocvar_g_forced_team_otherwise == "spectator")
2015  return 0;
2016  }
2017 
2018  if(this && (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR))
2019  return 0; // forced spectators can never join
2020 
2021  // TODO simplify this
2022  int totalClients = 0;
2023  int currentlyPlaying = 0;
2024  FOREACH_CLIENT(true, {
2025  if(it != ignore)
2026  ++totalClients;
2027  if(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || INGAME(it)))
2028  ++currentlyPlaying;
2029  });
2030 
2031  int player_limit = GetPlayerLimit();
2032 
2033  int free_slots = 0;
2034  if (!player_limit)
2035  free_slots = maxclients - totalClients;
2036  else if(player_limit > 0 && currentlyPlaying < player_limit)
2037  free_slots = min(maxclients - totalClients, player_limit - currentlyPlaying);
2038 
2039  static float msg_time = 0;
2040  if(this && !INGAME(this) && ignore && !free_slots && time > msg_time)
2041  {
2042  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
2043  msg_time = time + 0.5;
2044  }
2045 
2046  return free_slots;
2047 }
2048 
2050 {
2051  if(CS(this).motd_actived_time == 0)
2052  {
2053  if (autocvar_g_campaign) {
2054  if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) {
2055  CS(this).motd_actived_time = time;
2056  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_CAMPAIGN_MESSAGE, Campaign_GetMessage(), Campaign_GetLevelNum());
2057  }
2058  } else {
2059  if (PHYS_INPUT_BUTTON_INFO(this)) {
2060  CS(this).motd_actived_time = time;
2061  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
2062  }
2063  }
2064  }
2065  else if(CS(this).motd_actived_time > 0) // showing MOTD or campaign message
2066  {
2067  if (autocvar_g_campaign) {
2068  if (PHYS_INPUT_BUTTON_INFO(this))
2069  CS(this).motd_actived_time = time;
2070  else if ((time - CS(this).motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
2071  CS(this).motd_actived_time = 0;
2072  Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_CAMPAIGN_MESSAGE);
2073  }
2074  } else {
2075  if (PHYS_INPUT_BUTTON_INFO(this))
2076  CS(this).motd_actived_time = time;
2077  else if (time - CS(this).motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
2078  CS(this).motd_actived_time = 0;
2079  Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
2080  }
2081  }
2082  }
2083  else //if(CS(this).motd_actived_time < 0) // just connected, motd is active
2084  {
2085  if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD
2086  CS(this).motd_actived_time = -2; // wait until BUTTON_INFO gets released
2087  else if (CS(this).motd_actived_time == -2)
2088  {
2089  // instantly hide MOTD
2090  CS(this).motd_actived_time = 0;
2091  if (autocvar_g_campaign)
2092  Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_CAMPAIGN_MESSAGE);
2093  else
2094  Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
2095  }
2096  else if (IS_PLAYER(this) || IS_SPEC(this))
2097  {
2098  // FIXME occasionally for some reason MOTD never goes away
2099  // delay MOTD removal a little bit in the hope it fixes this bug
2100  if (CS(this).motd_actived_time == -1) // MOTD marked to fade away as soon as client becomes player or spectator
2101  CS(this).motd_actived_time = -(5 + floor(random() * 10)); // add small delay
2102  else //if (CS(this).motd_actived_time < -2)
2103  CS(this).motd_actived_time++;
2104  }
2105  }
2106 }
2107 
2109 {
2110  if (CS(this).version_mismatch) return false;
2111  if (time < CS(this).jointime + MIN_SPEC_TIME) return false;
2112  if (!nJoinAllowed(this, this)) return false;
2113  if (teamplay && lockteams) return false;
2114  if (MUTATOR_CALLHOOK(ForbidSpawn, this)) return false;
2115  if (ShowTeamSelection(this)) return false;
2116  return true;
2117 }
2118 
2122 {
2123  if (game_stopped || intermission_running) {
2124  this.modelflags &= ~MF_ROCKET;
2126  IntermissionThink(this);
2127  return false;
2128  }
2129 
2130  if (timeout_status == TIMEOUT_ACTIVE) {
2131  // don't allow the player to turn around while game is paused
2132  // FIXME turn this into CSQC stuff
2133  this.v_angle = this.lastV_angle;
2134  this.angles = this.lastV_angle;
2135  this.fixangle = true;
2136  }
2137 
2138  if (frametime) player_powerups(this);
2139 
2140  if (IS_DEAD(this)) {
2141  if (this.personal && g_race_qualifying) {
2142  if (time > this.respawn_time) {
2143  STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
2144  respawn(this);
2145  CS(this).impulse = CHIMPULSE_SPEEDRUN.impulse;
2146  }
2147  } else {
2148  if (frametime) player_anim(this);
2149 
2150  if (this.respawn_flags & RESPAWN_DENY)
2151  {
2152  STAT(RESPAWN_TIME, this) = 0;
2153  return false;
2154  }
2155 
2156  bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
2157 
2158  switch(this.deadflag)
2159  {
2160  case DEAD_DYING:
2161  {
2162  if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
2163  this.deadflag = DEAD_RESPAWNING;
2164  else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
2165  this.deadflag = DEAD_DEAD;
2166  break;
2167  }
2168  case DEAD_DEAD:
2169  {
2170  if (button_pressed)
2171  this.deadflag = DEAD_RESPAWNABLE;
2172  else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
2173  this.deadflag = DEAD_RESPAWNING;
2174  break;
2175  }
2176  case DEAD_RESPAWNABLE:
2177  {
2178  if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
2179  this.deadflag = DEAD_RESPAWNING;
2180  break;
2181  }
2182  case DEAD_RESPAWNING:
2183  {
2184  if (time > this.respawn_time)
2185  {
2186  this.respawn_time = time + 1; // only retry once a second
2187  this.respawn_time_max = this.respawn_time;
2188  respawn(this);
2189  }
2190  break;
2191  }
2192  }
2193 
2194  ShowRespawnCountdown(this);
2195 
2196  if (this.respawn_flags & RESPAWN_SILENT)
2197  STAT(RESPAWN_TIME, this) = 0;
2198  else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
2199  {
2200  if (time < this.respawn_time)
2201  STAT(RESPAWN_TIME, this) = this.respawn_time;
2202  else if (this.deadflag != DEAD_RESPAWNING)
2203  STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
2204  }
2205  else
2206  STAT(RESPAWN_TIME, this) = this.respawn_time;
2207  }
2208 
2209  // if respawning, invert stat_respawn_time to indicate this, the client translates it
2210  if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
2211  STAT(RESPAWN_TIME, this) *= -1;
2212 
2213  return false;
2214  }
2215 
2216  FixPlayermodel(this);
2217 
2218  if (this.shootfromfixedorigin != autocvar_g_shootfromfixedorigin) {
2219  this.shootfromfixedorigin = autocvar_g_shootfromfixedorigin;
2220  stuffcmd(this, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin));
2221  }
2222 
2223  // reset gun alignment when dual wielding status changes
2224  // to ensure guns are always aligned right and left
2225  bool dualwielding = W_DualWielding(this);
2226  if(this.dualwielding_prev != dualwielding)
2227  {
2228  W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
2229  this.dualwielding_prev = dualwielding;
2230  }
2231 
2232  // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
2233  //if(frametime)
2234  {
2235  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2236  {
2237  .entity weaponentity = weaponentities[slot];
2238  if(WEP_CVAR(vortex, charge_always))
2239  W_Vortex_Charge(this, weaponentity, frametime);
2240  W_WeaponFrame(this, weaponentity);
2241  }
2242  }
2243 
2244  if (frametime)
2245  {
2246  // WEAPONTODO: Add a weapon request for this
2247  // rot vortex charge to the charge limit
2248  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2249  {
2250  .entity weaponentity = weaponentities[slot];
2251  if (WEP_CVAR(vortex, charge_rot_rate) && this.(weaponentity).vortex_charge > WEP_CVAR(vortex, charge_limit) && this.(weaponentity).vortex_charge_rottime < time)
2252  this.(weaponentity).vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.(weaponentity).vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
2253  }
2254 
2255  player_regen(this);
2256  player_anim(this);
2258  }
2259 
2260  monsters_setstatus(this);
2261 
2262  return true;
2263 }
2264 
2266 // merged SpectatorThink and ObserverThink (old names are here so you can grep for them)
2268 {
2269  bool is_spec = IS_SPEC(this);
2270  if ( CS(this).impulse )
2271  {
2272  int r = MinigameImpulse(this, CS(this).impulse);
2273  if (!is_spec || r)
2274  CS(this).impulse = 0;
2275 
2276  if (is_spec && CS(this).impulse == IMP_weapon_drop.impulse)
2277  {
2278  STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3;
2279  CS(this).impulse = 0;
2280  return;
2281  }
2282  }
2283 
2284  if (this.flags & FL_JUMPRELEASED) {
2285  if (PHYS_INPUT_BUTTON_JUMP(this) && (joinAllowed(this) || time < CS(this).jointime + MIN_SPEC_TIME)) {
2286  this.flags &= ~FL_JUMPRELEASED;
2287  this.flags |= FL_SPAWNING;
2288  } else if((is_spec && (PHYS_INPUT_BUTTON_ATCK(this) || CS(this).impulse == 10 || CS(this).impulse == 15 || CS(this).impulse == 18 || (CS(this).impulse >= 200 && CS(this).impulse <= 209)))
2289  || (!is_spec && ((PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch) || this.would_spectate))) {
2290  this.flags &= ~FL_JUMPRELEASED;
2291  if(SpectateNext(this)) {
2292  TRANSMUTE(Spectator, this);
2293  } else if (is_spec) {
2294  TRANSMUTE(Observer, this);
2295  PutClientInServer(this);
2296  }
2297  else
2298  this.would_spectate = false; // unable to spectate anyone
2299  if (is_spec)
2300  CS(this).impulse = 0;
2301  } else if (is_spec) {
2302  if(CS(this).impulse == 12 || CS(this).impulse == 16 || CS(this).impulse == 19 || (CS(this).impulse >= 220 && CS(this).impulse <= 229)) {
2303  this.flags &= ~FL_JUMPRELEASED;
2304  if(SpectatePrev(this)) {
2305  TRANSMUTE(Spectator, this);
2306  } else {
2307  TRANSMUTE(Observer, this);
2308  PutClientInServer(this);
2309  }
2310  CS(this).impulse = 0;
2311  } else if(PHYS_INPUT_BUTTON_ATCK2(this)) {
2312  this.would_spectate = false;
2313  this.flags &= ~FL_JUMPRELEASED;
2314  TRANSMUTE(Observer, this);
2315  PutClientInServer(this);
2316  } else if(!SpectateUpdate(this) && !SpectateNext(this)) {
2317  PutObserverInServer(this, false, true);
2318  this.would_spectate = true;
2319  }
2320  }
2321  else {
2322  bool wouldclip = CS_CVAR(this).cvar_cl_clippedspectating;
2323  if (PHYS_INPUT_BUTTON_USE(this))
2324  wouldclip = !wouldclip;
2325  int preferred_movetype = (wouldclip ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
2326  set_movetype(this, preferred_movetype);
2327  }
2328  } else { // jump pressed
2329  if ((is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)))
2330  || (!is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this)))) {
2331  this.flags |= FL_JUMPRELEASED;
2332  if(this.flags & FL_SPAWNING)
2333  {
2334  this.flags &= ~FL_SPAWNING;
2335  if(joinAllowed(this))
2336  Join(this);
2337  else if(time < CS(this).jointime + MIN_SPEC_TIME)
2338  CS(this).autojoin_checked = -1;
2339  return;
2340  }
2341  }
2342  if(is_spec && !SpectateUpdate(this))
2343  PutObserverInServer(this, false, true);
2344  }
2345  if (is_spec)
2346  this.flags |= FL_CLIENT | FL_NOTARGET;
2347 }
2348 
2350 {
2351  if (!IS_PLAYER(this))
2352  return;
2353 
2354  if(this.vehicle)
2355  {
2356  if(!game_stopped)
2357  {
2358  vehicles_exit(this.vehicle, VHEF_NORMAL);
2359  return;
2360  }
2361  }
2362  else if(autocvar_g_vehicles_enter)
2363  {
2364  if(!game_stopped && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
2365  {
2366  entity head, closest_target = NULL;
2367  head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true);
2368 
2369  while(head) // find the closest acceptable target to enter
2370  {
2371  if(IS_VEHICLE(head) && !IS_DEAD(head) && head.takedamage != DAMAGE_NO)
2372  if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this)))
2373  {
2374  if(closest_target)
2375  {
2376  if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin))
2377  { closest_target = head; }
2378  }
2379  else { closest_target = head; }
2380  }
2381 
2382  head = head.chain;
2383  }
2384 
2385  if(closest_target) { vehicles_enter(this, closest_target); return; }
2386  }
2387  }
2388 
2389  // a use key was pressed; call handlers
2391 }
2392 
2393 
2394 /*
2395 =============
2396 PlayerPreThink
2397 
2398 Called every frame for each client before the physics are run
2399 =============
2400 */
2403 {
2404  STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO
2405  STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump;
2406 
2407  WarpZone_PlayerPhysics_FixVAngle(this);
2408 
2409  if (frametime) {
2410  // physics frames: update anticheat stuff
2411  anticheat_prethink(this);
2412 
2413  // WORKAROUND: only use dropclient in server frames (frametime set).
2414  // Never use it in cl_movement frames (frametime zero).
2415  if (blockSpectators && IS_REAL_CLIENT(this)
2416  && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this)
2418  {
2419  if (dropclient_schedule(this))
2420  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
2421  }
2422  }
2423 
2424  zoomstate_set = false;
2425 
2426  // Check for nameless players
2427  if (this.netname == "" || this.netname != CS(this).netname_previous)
2428  {
2429  bool assume_unchanged = (CS(this).netname_previous == "");
2430  if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength)
2431  {
2432  int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol);
2433  this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7"));
2434  sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength));
2435  assume_unchanged = false;
2436  // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
2437  }
2438  if (isInvisibleString(this.netname))
2439  {
2440  this.netname = strzone(sprintf("Player#%d", this.playerid));
2441  sprint(this, "Warning: invisible names are not allowed.\n");
2442  assume_unchanged = false;
2443  // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
2444  }
2445  if (!assume_unchanged && autocvar_sv_eventlog)
2446  GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false)));
2447  strcpy(CS(this).netname_previous, this.netname);
2448  }
2449 
2450  // version nagging
2451  if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime) {
2452  CS(this).version_nagtime = 0;
2453  if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0) {
2454  // git client
2455  } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
2456  // git server
2457  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2458  } else {
2459  int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
2460  if (r < 0) { // old client
2461  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2462  } else if (r > 0) { // old server
2463  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2464  }
2465  }
2466  }
2467 
2468  // GOD MODE info
2469  if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
2470  {
2471  Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
2472  this.max_armorvalue = 0;
2473  }
2474 
2475  if (frametime && IS_PLAYER(this) && time >= game_starttime)
2476  {
2477  if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
2478  {
2479  STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1);
2480  SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health));
2481  if (this.iceblock)
2482  this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
2483 
2484  if (STAT(REVIVE_PROGRESS, this) >= 1)
2485  Unfreeze(this, false);
2486  }
2487  else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING)
2488  {
2489  STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1);
2490  SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this)));
2491 
2492  if (GetResource(this, RES_HEALTH) < 1)
2493  {
2494  if (this.vehicle)
2495  vehicles_exit(this.vehicle, VHEF_RELEASE);
2496  if(this.event_damage)
2497  this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0');
2498  }
2499  else if (STAT(REVIVE_PROGRESS, this) <= 0)
2500  Unfreeze(this, false);
2501  }
2502  }
2503 
2505 
2506  if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle)
2507  if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
2508  {
2509  FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it) && !IS_DEAD(it) && it.takedamage != DAMAGE_NO,
2510  {
2511  if(!it.owner)
2512  {
2513  if(!it.team || SAME_TEAM(this, it))
2514  Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
2515  else if(autocvar_g_vehicles_steal)
2516  Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
2517  }
2518  else if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this))
2519  {
2520  Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
2521  }
2522  });
2523 
2524  this.last_vehiclecheck = time + 1;
2525  }
2526 
2527  if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed)
2528  PlayerUseKey(this);
2529  CS(this).usekeypressed = PHYS_INPUT_BUTTON_USE(this);
2530 
2531  if (IS_REAL_CLIENT(this))
2532  PrintWelcomeMessage(this);
2533 
2534  if (IS_PLAYER(this)) {
2535  if (IS_REAL_CLIENT(this) && time < CS(this).jointime + MIN_SPEC_TIME)
2536  error("Client can't be spawned as player on connection!");
2537  if(!PlayerThink(this))
2538  return;
2539  }
2540  else if (game_stopped || intermission_running) {
2542  IntermissionThink(this);
2543  return;
2544  }
2545  else if (IS_REAL_CLIENT(this) && CS(this).autojoin_checked <= 0 && time >= CS(this).jointime + MIN_SPEC_TIME)
2546  {
2547  bool early_join_requested = (CS(this).autojoin_checked < 0);
2548  CS(this).autojoin_checked = 1;
2549  // don't do this in ClientConnect
2550  // many things can go wrong if a client is spawned as player on connection
2551  if (early_join_requested || MUTATOR_CALLHOOK(AutoJoinOnConnection, this)
2554  {
2555  campaign_bots_may_start = true;
2556  if(joinAllowed(this))
2557  Join(this);
2558  return;
2559  }
2560  }
2561  else if (IS_OBSERVER(this) || IS_SPEC(this)) {
2563  }
2564 
2565  // WEAPONTODO: Add weapon request for this
2566  if (!zoomstate_set) {
2567  bool wep_zoomed = false;
2568  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2569  {
2570  .entity weaponentity = weaponentities[slot];
2571  Weapon thiswep = this.(weaponentity).m_weapon;
2572  if(thiswep != WEP_Null && thiswep.wr_zoom)
2573  wep_zoomed += thiswep.wr_zoom(thiswep, this);
2574  }
2575  SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed);
2576  }
2577 
2578  if (CS(this).teamkill_soundtime && time > CS(this).teamkill_soundtime)
2579  {
2580  CS(this).teamkill_soundtime = 0;
2581 
2582  entity e = CS(this).teamkill_soundsource;
2583  entity oldpusher = e.pusher;
2584  e.pusher = this;
2585  PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY);
2586  e.pusher = oldpusher;
2587  }
2588 
2589  if (CS(this).taunt_soundtime && time > CS(this).taunt_soundtime) {
2590  CS(this).taunt_soundtime = 0;
2591  PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT);
2592  }
2593 
2594  target_voicescript_next(this);
2595 }
2596 
2598 {
2599  if(IS_DEAD(this) || game_stopped || time < game_starttime || this.vehicle
2600  || STAT(FROZEN, this) || this.watertype != CONTENT_WATER)
2601  {
2602  STAT(AIR_FINISHED, this) = 0;
2603  return;
2604  }
2605 
2606  if (this.waterlevel != WATERLEVEL_SUBMERGED)
2607  {
2608  if(STAT(AIR_FINISHED, this) && STAT(AIR_FINISHED, this) < time)
2609  PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
2610  STAT(AIR_FINISHED, this) = 0;
2611  }
2612  else
2613  {
2614  if (!STAT(AIR_FINISHED, this))
2615  STAT(AIR_FINISHED, this) = time + autocvar_g_balance_contents_drowndelay;
2616  if (STAT(AIR_FINISHED, this) < time)
2617  { // drown!
2618  if (this.pain_finished < time)
2619  {
2621  this.pain_finished = time + 0.5;
2622  }
2623  }
2624  }
2625 }
2626 
2628 
2630 {
2631  this.movetype = (this.move_qcphysics) ? MOVETYPE_QCPLAYER : this.move_movetype;
2632 
2633  if(!this.move_qcphysics)
2634  return;
2635 
2636  if(!frametime && !CS(this).pm_frametime)
2637  return;
2638 
2639  Movetype_Physics_NoMatchTicrate(this, CS(this).pm_frametime, true);
2640 
2641  CS(this).pm_frametime = 0;
2642 }
2643 
2644 /*
2645 =============
2646 PlayerPostThink
2647 
2648 Called every frame for each client after the physics are run
2649 =============
2650 */
2652 {
2653  Player_Physics(this);
2654 
2656  if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
2657  if (IS_REAL_CLIENT(this))
2659  if (!intermission_running) // NextLevel() kills all centerprints after setting this true
2660  {
2661  int totalClients = 0;
2663  {
2664  // maxidle disabled in local matches by not counting clients (totalClients 0)
2665  if (server_is_dedicated)
2666  {
2668  {
2669  ++totalClients;
2670  });
2671  if (maxclients - totalClients > autocvar_sv_maxidle_slots)
2672  totalClients = 0;
2673  }
2674  }
2675  else if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
2676  {
2678  {
2679  ++totalClients;
2680  });
2681  }
2682 
2683  if (totalClients < autocvar_sv_maxidle_minplayers)
2684  {
2685  // idle kick disabled
2686  CS(this).parm_idlesince = time;
2687  }
2688  else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
2689  {
2690  if (CS(this).idlekick_lasttimeleft)
2691  {
2692  CS(this).idlekick_lasttimeleft = 0;
2693  Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
2694  }
2695  }
2696  else
2697  {
2698  float maxidle_time = autocvar_sv_maxidle;
2701  float timeleft = ceil(maxidle_time - (time - CS(this).parm_idlesince));
2702  float countdown_time = max(min(10, maxidle_time - 1), ceil(maxidle_time * 0.33)); // - 1 to support maxidle_time <= 10
2703  if (timeleft == countdown_time && !CS(this).idlekick_lasttimeleft)
2704  {
2706  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOVETOSPEC_IDLING, timeleft);
2707  else
2708  Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
2709  }
2710  if (timeleft <= 0) {
2712  {
2713  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING, this.netname, maxidle_time);
2714  PutObserverInServer(this, true, true);
2715  }
2716  else
2717  {
2718  if (dropclient_schedule(this))
2719  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname, maxidle_time);
2720  }
2721  return;
2722  }
2723  else if (timeleft <= countdown_time) {
2724  if (timeleft != CS(this).idlekick_lasttimeleft)
2725  play2(this, SND(TALK2));
2726  CS(this).idlekick_lasttimeleft = timeleft;
2727  }
2728  }
2729  }
2730 
2731  CheatFrame(this);
2732 
2733  if (game_stopped)
2734  {
2735  this.solid = SOLID_NOT;
2736  this.takedamage = DAMAGE_NO;
2737  set_movetype(this, MOVETYPE_NONE);
2738  CS(this).teamkill_complain = 0;
2739  CS(this).teamkill_soundtime = 0;
2740  CS(this).teamkill_soundsource = NULL;
2741  }
2742 
2743  if (IS_PLAYER(this)) {
2744  if(this.death_time == time && IS_DEAD(this))
2745  {
2746  // player's bbox gets resized now, instead of in the damage event that killed the player,
2747  // once all the damage events of this frame have been processed with normal size
2748  this.maxs.z = 5;
2749  setsize(this, this.mins, this.maxs);
2750  }
2751  DrownPlayer(this);
2752  UpdateChatBubble(this);
2753  if (CS(this).impulse) ImpulseCommands(this);
2754  GetPressedKeys(this);
2755  if (game_stopped)
2756  {
2757  CSQCMODEL_AUTOUPDATE(this);
2758  return;
2759  }
2760  }
2761  else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
2762  {
2763  CS(this).pressedkeys = 0;
2764  STAT(PRESSED_KEYS, this) = 0;
2765  }
2766 
2767  if (this.waypointsprite_attachedforcarrier) {
2768  float hp = healtharmor_maxdamage(GetResource(this, RES_HEALTH), GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x;
2769  WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, hp);
2770  }
2771 
2772  CSQCMODEL_AUTOUPDATE(this);
2773 }
2774 
2775 // hack to copy the button fields from the client entity to the Client State
2776 void PM_UpdateButtons(entity this, entity store)
2777 {
2778  if(this.impulse)
2779  store.impulse = this.impulse;
2780  this.impulse = 0;
2781 
2782  bool typing = this.buttonchat || this.button12;
2783 
2784  store.button0 = (typing) ? 0 : this.button0;
2785  //button1?!
2786  store.button2 = (typing) ? 0 : this.button2;
2787  store.button3 = (typing) ? 0 : this.button3;
2788  store.button4 = this.button4;
2789  store.button5 = (typing) ? 0 : this.button5;
2790  store.button6 = this.button6;
2791  store.button7 = this.button7;
2792  store.button8 = this.button8;
2793  store.button9 = this.button9;
2794  store.button10 = this.button10;
2795  store.button11 = this.button11;
2796  store.button12 = this.button12;
2797  store.button13 = this.button13;
2798  store.button14 = this.button14;
2799  store.button15 = this.button15;
2800  store.button16 = this.button16;
2801  store.buttonuse = this.buttonuse;
2802  store.buttonchat = this.buttonchat;
2803 
2804  store.cursor_active = this.cursor_active;
2805  store.cursor_screen = this.cursor_screen;
2806  store.cursor_trace_start = this.cursor_trace_start;
2807  store.cursor_trace_endpos = this.cursor_trace_endpos;
2808  store.cursor_trace_ent = this.cursor_trace_ent;
2809 
2810  store.ping = this.ping;
2811  store.ping_packetloss = this.ping_packetloss;
2812  store.ping_movementloss = this.ping_movementloss;
2813 
2814  store.v_angle = this.v_angle;
2815  store.movement = this.movement;
2816 }
2817 
2818 NET_HANDLE(fpsreport, bool)
2819 {
2820  int fps = ReadShort();
2821  PlayerScore_Set(sender, SP_FPS, fps);
2822  return true;
2823 }
#define INGAME(it)
Definition: sv_rules.qh:20
const float SOLID_NOT
Definition: csprogsdefs.qc:244
entity random_start_ammo
Entity that contains amount of ammo to give with random start weapons.
Definition: world.qh:97
const int CH_VOICE
Definition: sound.qh:10
int autocvar_sv_name_maxlength
Definition: client.qh:52
float autocvar_g_balance_armor_rotlinear
Definition: sv_resources.qh:18
float autocvar_g_balance_pause_health_regen_spawn
Definition: client.qh:13
#define CSQCMODEL_EF_RESPAWNGHOST
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition: player.qh:148
float DEAD_DYING
Definition: progsdefs.qc:275
ERASEABLE void IL_REMOVE(IntrusiveList this, entity it)
Remove any element, anywhere in the list.
entity SelectSpawnPoint(entity this, bool anypoint)
Definition: spawnpoints.qc:329
void respawn(entity this)
Definition: client.qc:1417
const int RESPAWN_FORCE
Definition: client.qh:330
#define WEPSET(id)
Definition: all.qh:37
bool would_spectate
Definition: client.qc:2265
string getwelcomemessage(entity this)
Definition: client.qc:1036
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition: player.qh:147
#define assert(expr,...)
Definition: log.qh:8
bool PlayerHeal(entity targ, entity inflictor, float amount, float limit)
Definition: player.qc:608
#define IL_EACH(this, cond, body)
float autocvar_g_balance_pause_health_rot_spawn
Definition: client.qh:14
float dmg_take
Definition: view.qh:119
float model_randomizer
Definition: client.qc:422
int autocvar_sv_defaultplayerskin_pink
Definition: player.qh:9
float MOVETYPE_NONE
Definition: progsdefs.qc:246
const int RESPAWN_DENY
Definition: client.qh:332
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
float start_ammo_rockets
Definition: world.qh:87
const float SOLID_SLIDEBOX
Definition: csprogsdefs.qc:247
WepSet WEPSET_SUPERWEAPONS
Definition: all.qh:307
void ReadyCount()
Definition: vote.qc:498
int serverflags
Definition: main.qh:184
void SpectateCopy(entity this, entity spectatee)
Definition: client.qc:1713
float weapon_load[REGISTRY_MAX(Weapons)]
Definition: weaponsystem.qh:29
bool zoomstate
Definition: client.qh:72
this frame
Definition: client.qc:85
float autocvar_g_balance_superweapons_time
Definition: items.qh:6
float alpha
Definition: items.qc:14
void ClientInit_CheckUpdate(entity this)
Definition: client.qc:879
void play_countdown(entity this, float finished, Sound samp)
Definition: client.qc:1447
float MOVETYPE_WALK
Definition: progsdefs.qc:249
string string_null
Definition: nil.qh:9
const int NUM_TEAM_2
Definition: teams.qh:19
const int WEP_FLAG_RELOADABLE
Definition: weapon.qh:201
void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, entity ammo_entity)
Give several random weapons and ammo to the entity.
Definition: items.qc:406
bool monster_attack
Definition: sv_monsters.qh:62
IntrusiveList g_monster_targets
Definition: sv_monsters.qh:147
float autocvar_sv_player_scale
Definition: client.qh:56
bool SetPlayerTeam(entity player, int team_index, int type)
Sets the team of the player.
Definition: teamplay.qc:237
entity world
Definition: csprogsdefs.qc:15
const int SPECIES_HUMAN
Definition: constants.qh:20
IntrusiveList g_conveyed
Definition: conveyor.qh:4
bool autocvar_g_nodepthtestplayers
Definition: client.qh:33
float respawn_countdown
Definition: client.qh:328
TRANSMUTE(Player, this)
skin
Definition: ent_cs.qc:143
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 CPID_CAMPAIGN_MESSAGE
Definition: all.inc:715
#define SND(id)
Definition: all.qh:35
float autocvar_g_respawn_ghosts_speed
Definition: client.qh:31
float g_nexball_meter_period
Definition: sv_nexball.qh:54
float watertype
Definition: progsdefs.qc:182
void ClientConnect(entity this)
ClientConnect
Definition: client.qc:1096
vector colormod
Definition: powerups.qc:21
const int WATERLEVEL_SUBMERGED
Definition: movetypes.qh:14
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
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition: player.qh:151
float autocvar_g_balance_contents_damagerate
Definition: main.qh:3
const int MF_ROCKET
void W_WeaponFrame(Player actor,.entity weaponentity)
float get_model_parameters(string m, float sk)
Definition: util.qc:1258
float button3
bool wasplayer
Definition: client.qh:69
float start_ammo_fuel
Definition: world.qh:90
int autocvar_sv_defaultplayerskin_blue
Definition: player.qh:8
float warmup_start_ammo_shells
Definition: world.qh:104
string autocvar_g_forced_team_otherwise
Definition: teamplay.qh:11
float autocvar_g_balance_contents_drowndelay
Definition: main.qh:4
const int VOICETYPE_PLAYERSOUND
Definition: globalsound.qh:64
void player_regen(entity this)
Definition: client.qc:1601
int respawn_flags
Definition: client.qh:324
int int number
Definition: impulse.qc:89
const int TELEPORT_SIMPLE
Definition: teleporters.qh:27
Header file that describes the resource system.
bool iscreature
Definition: main.qh:42
float button12
float autocvar_g_balance_health_rot
Definition: sv_resources.qh:29
float waterlevel
Definition: progsdefs.qc:181
void bot_relinkplayerlist()
Definition: bot.qc:381
WepSet start_weapons
Definition: world.qh:81
#define w_getbestweapon(ent, wepent)
Definition: selection.qh:23
Header file that describes the handicap system.
float autocvar_sv_maxidle
Definition: client.qh:36
float autocvar_g_balance_pause_armor_rot_spawn
Definition: client.qh:11
float blockSpectators
Definition: client.qh:334
#define IS_CLIENT(v)
Definition: utils.qh:13
const int TELEPORT_NORMAL
Definition: teleporters.qh:26
float respawn_time
Definition: client.qh:325
#define IS_DUCKED(s)
Definition: player.qh:206
int autocvar_g_balance_fuel_rotstable
Definition: sv_resources.qh:25
void bot_aim_reset(entity this)
Definition: aim.qc:170
#define IS_INDEPENDENT_PLAYER(e)
Definition: client.qh:314
void ChatBubbleThink(entity this)
Definition: client.qc:1267
int autocvar_sv_maxidle_minplayers
Definition: client.qh:37
float buttonchat
Definition: dpextensions.qc:36
int autocvar_sv_defaultplayerskin_red
Definition: player.qh:10
void SetChangeParms(entity this)
Definition: client.qc:921
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition: teamplay.qc:627
vector oldorigin
Definition: csprogsdefs.qc:102
float damageforcescale
Definition: damage.qh:137
vector cursor_screen
float last_vehiclecheck
Definition: client.qc:2401
string autocvar_sv_motd
Definition: client.qh:51
IntrusiveList g_damagedbycontents
Definition: damage.qh:155
void PlayerPostThink(entity this)
Definition: client.qc:2651
string autocvar_g_mutatormsg
Definition: client.qh:34
entity() spawn
float DAMAGE_AIM
Definition: progsdefs.qc:284
void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: player.qc:229
float bot_attack
Definition: api.qh:38
int player_count
Definition: api.qh:103
void navigation_dynamicgoal_init(entity this, bool initially_static)
Definition: navigation.qc:76
int playerid
Definition: client.qh:78
ClientState CS(Client this)
Definition: state.qh:47
void anticheat_spectatecopy(entity this, entity spectatee)
Definition: anticheat.qc:151
bool speedrunning
Definition: cheats.qh:21
float autocvar_g_respawn_ghosts_fadetime
Definition: client.qh:29
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
int autocvar_g_balance_armor_start
Definition: client.qh:10
vector v_angle
Definition: progsdefs.qc:161
float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
Definition: client.qc:1550
void PrintWelcomeMessage(entity this)
Definition: client.qc:2049
void part_minigame(entity player)
vector maxs
Definition: csprogsdefs.qc:113
vector hook_shotorigin[4]
Definition: main.qh:177
#define PHYS_INPUT_BUTTON_ZOOM(s)
Definition: player.qh:149
#define IS_OBSERVER(v)
Definition: utils.qh:11
bool autocvar_sv_defaultcharacterskin
Definition: player.qh:7
int GetPlayerLimit()
Definition: client.qc:1990
#define CS_CVAR(this)
Definition: state.qh:51
bool PlayerInIPList(entity p, string iplist)
Definition: client.qc:977
string autocvar_sv_defaultplayermodel
Definition: player.qh:13
bool SpectateUpdate(entity this)
Definition: client.qc:1784
void PlayerState_attach(entity this)
Definition: state.qc:12
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition: triggers.qc:366
float DPCONTENTS_BOTCLIP
float button6
bool warmup_stage
Definition: main.qh:103
void player_powerups(entity this)
Definition: client.qc:1470
string netname
Definition: powerups.qc:20
#define PS(this)
Definition: state.qh:18
bool ready
Definition: main.qh:74
#define WEP_CVAR(wepname, name)
Definition: all.qh:299
entity iceblock
Definition: damage.qh:116
int autocvar_spawn_debug
Definition: client.qh:50
bool Spectate(entity this, entity pl)
Definition: client.qc:1873
void UpdatePlayerSounds(entity this)
Definition: globalsound.qc:300
float pushltime
Definition: jumppads.qh:10
bool team_selected
Definition: client.qc:1958
Effect is being removed by a function, calls regular removal mechanics.
Definition: all.qh:25
int start_items
Definition: world.qh:84
void W_ResetGunAlign(entity player, int preferred_alignment)
float maxclients
Definition: csprogsdefs.qc:21
const int VOICETYPE_AUTOTAUNT
Definition: globalsound.qh:68
float dmg_team
Definition: damage.qh:56
float DPCONTENTS_PLAYERCLIP
bool autocvar_g_forced_respawn
Definition: client.qh:42
float autocvar_g_balance_armor_regenlinear
Definition: sv_resources.qh:15
float autocvar_g_respawn_ghosts_time
Definition: client.qh:30
entity to
Definition: self.qh:96
float vote_called
Definition: vote.qh:43
const int CH_PLAYER_SINGLE
Definition: sound.qh:21
void TeamBalance_JoinBestTeam(entity player)
Assigns the given player to a team that will make the game most balanced.
Definition: teamplay.qc:451
float start_ammo_shells
Definition: world.qh:85
string shootfromfixedorigin
Definition: client.qc:2119
bool autocvar_g_changeteam_banned
Definition: teamplay.qh:5
origin
Definition: ent_cs.qc:114
#define UNSET_DUCKED(s)
Definition: player.qh:208
float autocvar_g_player_damageforcescale
Definition: client.qh:20
const int EF_RESTARTANIM_BIT
void antilag_clear(entity e, entity store)
Definition: antilag.qc:114
const float TIMEOUT_ACTIVE
Definition: common.qh:49
float FL_GODMODE
Definition: progsdefs.qc:237
string g_weaponarena_list
Definition: world.qh:79
float ping
Definition: main.qh:138
#define MAKE_INDEPENDENT_PLAYER(e)
Definition: client.qh:315
vector cursor_trace_start
string classname
Definition: csprogsdefs.qc:107
int autocvar_bot_vs_human
Definition: cvars.qh:70
const float EF_NODEPTHTEST
Definition: csprogsdefs.qc:304
vector punchangle
vector oldvelocity
Definition: main.qh:38
vector avelocity
Definition: csprogsdefs.qc:105
bool zoomstate_set
Definition: client.qc:1670
string autocvar_sv_defaultplayermodel_blue
Definition: player.qh:14
float autocvar_g_balance_fuel_rotlinear
Definition: sv_resources.qh:24
NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew)
Definition: client.qc:28
void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)
Definition: movetypes.qc:813
bool dropclient_schedule(entity this)
Schedules dropclient for a player and returns true; if dropclient is already scheduled (for that play...
Definition: main.qc:42
bool move_qcphysics
Definition: client.qc:2627
bool autocvar_g_botclip_collisions
Definition: client.qh:15
#define INDEPENDENT_PLAYERS
Definition: client.qh:313
int killcount
Definition: client.qh:317
const int MAX_AXH
Definition: cl_vehicles.qh:23
bool g_race_qualifying
Definition: race.qh:12
void PlayerPreThink(entity this)
Definition: client.qc:2402
vector cursor_trace_endpos
float impulse
Definition: progsdefs.qc:158
float effects
Definition: csprogsdefs.qc:111
void bot_clientconnect(entity this)
Definition: bot.qc:426
float taunt_soundtime
Definition: damage.qh:62
#define DMG_NOWEP
Definition: damage.qh:126
int modelflags
void UpdateChatBubble(entity this)
Definition: client.qc:1293
entity owner
Definition: main.qh:73
void SetZoomState(entity this, float newzoom)
Definition: client.qc:1671
void vehicles_enter(entity pl, entity veh)
Definition: sv_vehicles.qc:939
bool autocvar_g_respawn_ghosts
Definition: client.qh:27
float g_weaponarena_random
Definition: world.qh:78
float warmup_start_ammo_plasma
Definition: world.qh:108
ClientDisconnect(this)
string model
Definition: csprogsdefs.qc:108
float move_movetype
Definition: movetypes.qh:76
void anticheat_prethink(entity this)
Definition: anticheat.qc:157
IntrusiveList g_initforplayer
Definition: client.qh:373
const int CH_INFO
Definition: sound.qh:6
bool ShowTeamSelection(entity this)
Definition: client.qc:1959
int autocvar_sv_defaultplayerskin
Definition: player.qh:18
string clientstuff
Definition: world.qh:55
string GetClientVersionMessage(entity this)
Definition: client.qc:1021
#define FOREACH_WORD(words, cond, body)
Definition: iter.qh:33
float DEAD_DEAD
Definition: progsdefs.qc:276
float autocvar_g_balance_fuel_rot
Definition: sv_resources.qh:23
IntrusiveList g_bot_targets
Definition: api.qh:149
const int WATERLEVEL_NONE
Definition: movetypes.qh:11
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
const int EF_TELEPORT_BIT
float compressShotOrigin(vector v)
Definition: util.qc:1121
void RotRegen(entity this, Resource res, float limit_mod, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime)
Definition: client.qc:1570
int Campaign_GetLevelNum()
Definition: campaign.qc:44
entity frozen_by
Definition: damage.qh:117
float autocvar_g_balance_health_rotlinear
Definition: sv_resources.qh:30
float autocvar_g_trueaim_minrange
Definition: tracing.qh:16
#define strcpy(this, s)
Definition: string.qh:49
float autocvar_g_balance_armor_rot
Definition: sv_resources.qh:17
int autocvar_sv_maxidle_slots
Definition: client.qh:40
bool SpawnEvent_Send(entity this, entity to, int sf)
Definition: spawnpoints.qc:32
#define setmodel(this, m)
Definition: model.qh:26
string autocvar_sv_defaultplayercolors
Definition: player.qh:12
#define MUTATOR_IS_ENABLED(this)
Definition: base.qh:176
RES_HEALTH
Definition: ent_cs.qc:126
bool autocvar_sv_teamnagger
Definition: client.qh:55
this team
Definition: client.qc:86
const float EF_NODRAW
Definition: csprogsdefs.qc:305
float autocvar_g_balance_health_regenstable
Definition: sv_resources.qh:28
#define CHAT_NOSPECTATORS()
Definition: chat.qh:27
const float CONTENT_EMPTY
Definition: csprogsdefs.qc:236
void Unfreeze(entity targ, bool reset_health)
Definition: damage.qc:546
float respawn_time_max
Definition: client.qh:326
const float EF_FULLBRIGHT
Definition: csprogsdefs.qc:303
string weaponmodel
Definition: progsdefs.qc:140
float button8
Effect is being forcibly removed without calling any additional mechanics.
Definition: all.qh:27
#define IS_SPEC(v)
Definition: utils.qh:10
Force the player to spectator team.
Definition: teamplay.qh:137
void IntermissionThink(entity this)
bool SpectateNext(entity this)
Definition: client.qc:1883
int autocvar_g_balance_armor_regenstable
Definition: sv_resources.qh:16
float button5
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition: bits.qh:8
float get_model_parameters_species
Definition: util.qh:140
bool autocvar_sv_maxidle_slots_countbots
Definition: client.qh:41
int q3compat
Definition: quake3.qh:3
entity personal
Definition: cheats.qh:23
Player is joining spectators. //TODO: Remove?
Definition: teamplay.qh:113
entity enemy
Definition: sv_ctf.qh:143
int TeamBalance_GetAllowedTeams(entity balance)
Returns the bitmask of allowed teams.
Definition: teamplay.qc:640
float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
Definition: util.qc:809
float button10
entity msg_entity
Definition: progsdefs.qc:63
vector mins
Definition: csprogsdefs.qc:113
float warmup_start_ammo_cells
Definition: world.qh:107
#define vlen2(v)
Definition: vector.qh:4
bool MinigameImpulse(entity this, int imp)
bool autocvar_g_weaponswitch_debug
Definition: selection.qh:7
float cnt
Definition: powerups.qc:24
#define g_cts
Definition: cts.qh:36
float dmg_save
Definition: progsdefs.qc:199
#define g_race
Definition: race.qh:46
int Player_GetForcedTeamIndex(entity player)
Returns the index of the forced team of the given player.
Definition: teamplay.qc:346
entity active_minigame
Definition: cl_minigames.qh:85
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
const int FRAGS_PLAYER
Definition: constants.qh:3
vector punchvector
entity conveyor
Definition: player.qh:54
bool autocvar__notarget
Definition: client.qh:9
entity AuxiliaryXhair[MAX_AXH]
Definition: cl_vehicles.qh:24
void Portal_ClearAll(entity own)
Definition: portals.qc:587
float damagedbycontents
Definition: damage.qh:48
entity dmg_inflictor
Definition: progsdefs.qc:200
float autocvar_g_balance_fuel_regen
Definition: sv_resources.qh:20
string autocvar_hostname
Definition: client.qh:49
void SetResource(entity e, Resource res_type, float amount)
Sets the current amount of resource the given entity will have.
Definition: cl_resources.qc:26
float warmup_start_ammo_fuel
Definition: world.qh:109
string autocvar_sv_defaultplayermodel_yellow
Definition: player.qh:17
bool autocvar_sv_maxidle_alsokickspectators
Definition: client.qh:39
string CheckPlayerModel(string plyermodel)
Definition: client.qc:194
bool W_DualWielding(entity player)
Definition: common.qc:20
int cursor_active
Definition: view.qh:108
#define WARMUP_START_WEAPONS
Definition: world.qh:103
float ping_packetloss
Definition: main.qh:138
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
void GetPressedKeys(entity this)
Definition: client.qc:1681
void monsters_setstatus(entity this)
Definition: sv_monsters.qc:33
bool just_joined
Definition: client.qh:74
#define PHYS_INPUT_BUTTON_USE(s)
Definition: player.qh:154
int autocvar_sv_timeout_number
Definition: common.qh:8
#define g_assault
Definition: assault.qh:27
void PlayerUseKey(entity this)
Definition: client.qc:2349
float pauserothealth_finished
Definition: client.qh:343
float start_ammo_cells
Definition: world.qh:88
float armortype
Definition: progsdefs.qc:178
float CheatFrame(entity this)
Definition: cheats.qc:703
int autocvar_sv_defaultplayerskin_yellow
Definition: player.qh:11
bool joinAllowed(entity this)
Definition: client.qc:2108
void player_anim(entity this)
Definition: player.qc:152
const float CONTENT_WATER
Definition: csprogsdefs.qc:238
ERASEABLE int vercmp(string v1, string v2)
Definition: string.qh:488
float fixangle
Definition: progsdefs.qc:160
float motd_actived_time
Definition: client.qh:67
float autocvar_sv_maxidle_playertospectator
Definition: client.qh:38
int autocvar_g_balance_armor_rotstable
Definition: sv_resources.qh:19
float autocvar_g_balance_fuel_regenlinear
Definition: sv_resources.qh:21
float CalcRot(float current, float stable, float rotfactor, float rotframetime)
Definition: client.qc:1560
float pauserotfuel_finished
Definition: client.qh:345
float MOVETYPE_FLY_WORLDONLY
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition: client.qc:238
void send_CSQC_teamnagger()
Definition: client.qc:97
float teamkill_soundtime
Definition: damage.qh:58
string FallbackPlayerModel
Definition: client.qc:193
float button4
bool autocvar_sv_q3compat_changehitbox
Definition: quake3.qh:7
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
float spectatortime
Definition: client.qh:335
float FL_JUMPRELEASED
Definition: progsdefs.qc:243
int autocvar_sv_spectate
Definition: client.qh:54
string netaddress
int player_getspecies(entity this)
Definition: client.qc:413
float DPCONTENTS_SOLID
#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s)
Definition: player.qh:157
const float VOL_BASE
Definition: sound.qh:36
float pauserotarmor_finished
Definition: client.qh:344
void ShowRespawnCountdown(entity this)
Definition: client.qc:1939
#define TC(T, sym)
Definition: _all.inc:82
frags
Definition: ent_cs.qc:151
void player_setupanimsformodel(entity this)
Definition: player.qc:145
float PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
Definition: scores.qc:267
float jointime
Definition: client.qh:64
bool Ban_MaybeEnforceBanOnce(entity client)
Definition: ipban.qc:471
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition: player.qh:146
#define strstrofs
Definition: dpextensions.qh:42
int autocvar_g_antilag
Definition: antilag.qh:3
Definition: client.qh:88
float takedamage
Definition: progsdefs.qc:147
float button7
float max_armorvalue
Definition: items.qh:31
int AvailableTeams()
Definition: scores_rules.qc:22
float autocvar_g_ballistics_density_player
Definition: tracing.qh:7
void GameLogEcho(string s)
Definition: gamelog.qc:12
void DecodeLevelParms(entity this)
Definition: client.qc:934
bool autocvar_sv_defaultcharacter
Definition: player.qh:6
bool autocvar_sv_showspectators
Definition: client.qh:57
#define setcolor
Definition: pre.qh:11
string autocvar_g_random_start_weapons
Holds a list of possible random start weapons.
Definition: world.qh:94
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: damage.qc:583
void SetSpectatee(entity this, entity spectatee)
Definition: client.qc:1836
float alivetime
Definition: client.qh:66
#define SAME_TEAM(a, b)
Definition: teams.qh:239
Definition: sound.qh:119
float teamplay
Definition: progsdefs.qc:31
float disableclientprediction
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition: common.qc:669
const float ATTEN_NORM
Definition: sound.qh:30
float nextthink
Definition: csprogsdefs.qc:121
int autocvar_g_balance_fuel_regenstable
Definition: sv_resources.qh:22
const int CH_TRIGGER_SINGLE
Definition: sound.qh:13
#define BITSET(var, mask, flag)
Definition: bits.qh:11
float autocvar_g_balance_health_regen
Definition: sv_resources.qh:26
float autocvar_g_balance_damagepush_speedfactor
Definition: damage.qh:18
float warmup_start_armorvalue
Definition: world.qh:111
float GetResourceLimit(entity e, Resource res_type)
Returns the maximum amount of the given resource.
Definition: sv_resources.qc:12
float scale
Definition: projectile.qc:14
void RemoveGrapplingHooks(entity pl)
Definition: hook.qc:78
bool lockteams
Definition: teamplay.qh:13
float DEAD_RESPAWNING
Definition: progsdefs.qc:278
void DrownPlayer(entity this)
Definition: client.qc:2597
vector movement
string clientstatus
void Player_Physics(entity this)
Definition: client.qc:2629
entity ladder_entity
Definition: ladder.qh:11
void PM_UpdateButtons(entity this, entity store)
Definition: client.qc:2776
bool campaign_bots_may_start
campaign mode: bots shall spawn but wait for the player to spawn before they do anything in other gam...
Definition: campaign.qh:27
#define stuffcmd(cl,...)
Definition: progsdefs.qh:23
float autocvar_gameversion
Definition: client.qh:46
vector(float skel, float bonenum) _skel_get_boneabs_hidden
void WriteSpectators(entity player, entity to)
Definition: client.qc:115
#define IS_VEHICLE(v)
Definition: utils.qh:22
#define tokenize_console
Definition: dpextensions.qh:24
void SetSpectatee_status(entity this, int spectatee_num)
Definition: client.qc:1819
const int NUM_TEAM_4
Definition: teams.qh:21
entity cursor_trace_ent
int fragsfilter_cnt
Definition: quake3.qh:12
float button13
float button16
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 CPID_IDLING
Definition: all.inc:654
string GameLog_ProcessIP(string s)
Definition: gamelog.qc:5
bool autocvar_g_fullbrightplayers
Definition: client.qh:16
const int FROZEN_TEMP_REVIVING
Definition: damage.qh:110
entity viewloc
Definition: viewloc.qh:13
bool dualwielding_prev
Definition: client.qc:2120
entity killindicator
Definition: clientkill.qh:7
float MOVETYPE_NOCLIP
Definition: progsdefs.qc:254
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
const int VHF_MULTISLOT
Add random head angles each frame if health < 50%.
Definition: vehicle.qh:75
float deadflag
Definition: progsdefs.qc:149
float spectatee_status
Definition: main.qh:166
float autocvar_sv_foginterval
Definition: client.qh:35
float pain_finished
float DPCONTENTS_BODY
float start_health
Definition: world.qh:98
float flags
Definition: csprogsdefs.qc:129
entity drawonlytoclient
const string STR_PLAYER
Definition: utils.qh:5
float count
Definition: powerups.qc:22
bool istypefrag
Definition: jumppads.qh:11
float race_completed
Definition: race.qh:25
void CL_SpawnWeaponentity(entity actor,.entity weaponentity)
float warmup_start_ammo_rockets
Definition: world.qh:106
float autocvar_g_maxplayers_spectator_blocktime
Definition: client.qh:44
void ClientData_Touch(entity e)
Definition: client.qc:171
float button0
Definition: progsdefs.qc:154
void ClientData_Attach(entity this)
Definition: client.qc:158
float fade_time
Definition: common.qh:22
float button2
Definition: progsdefs.qc:156
void ONREMOVE(entity this)
bool sv_ready_restart_after_countdown
Definition: world.qh:119
void calculate_player_respawn_time(entity this)
Definition: client.qc:1315
const int SVC_SETVIEW
Definition: client.qh:339
entity clientdata
Definition: client.qh:62
float items
Definition: progsdefs.qc:145
float parm1
Definition: progsdefs.qc:45
float ping_movementloss
Definition: main.qh:138
void PutPlayerInServer(entity this)
Definition: client.qc:535
void race_InitSpectator()
Definition: race.qc:71
int autocvar_g_balance_contents_playerdamage_drowning
Definition: main.qh:5
string autocvar_sv_defaultplayermodel_red
Definition: player.qh:16
float g_weapon_stay
Definition: world.qh:112
const int FRAGS_SPECTATOR
Definition: constants.qh:4
float start_ammo_nails
Definition: world.qh:86
const int CH_PLAYER
Definition: sound.qh:20
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
bool drag_undraggable(entity draggee, entity dragger)
Definition: cheats.qc:897
float button14
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
void FixPlayermodel(entity player)
Definition: client.qc:423
bool ClientData_Send(entity this, entity to, int sf)
Definition: client.qc:129
#define vec2(...)
Definition: vector.qh:90
bool PlayerThink(entity this)
Definition: client.qc:2121
bool autocvar_g_jetpack
Definition: world.qh:8
entity weaponentities[MAX_WEAPONSLOTS]
Definition: weapon.qh:14
float warmup_start_health
Definition: world.qh:110
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
float teleportable
Definition: teleporters.qh:24
setorigin(ent, v)
float dphitcontentsmask
bool Player_HasRealForcedTeam(entity player)
Returns whether player has real forced team.
Definition: teamplay.qc:341
bool autocvar_g_balance_teams
Definition: teamplay.qh:8
#define STATIC_METHOD(cname, name, prototype)
Definition: oo.qh:266
float DEAD_NO
Definition: progsdefs.qc:274
float pauseregen_finished
Definition: client.qh:342
void setplayermodel(entity e, string modelname)
Definition: client.qc:228
#define setthink(e, f)
void Player_DetermineForcedTeam(entity player)
Determines the forced team of the player using current global config.
Definition: teamplay.qc:376
const float VOL_BASEVOICE
Definition: sound.qh:37
const int MOVETYPE_QCPLAYER
Definition: movetypes.qh:151
bool intermission_running
Definition: intermission.qh:9
void SUB_SetFade(entity ent, float vanish_time, float fading_time)
Definition: subs.qc:77
#define strfree(this)
Definition: string.qh:56
int nJoinAllowed(entity this, entity ignore)
Determines whether the player is allowed to join.
Definition: client.qc:2006
float vortex_charge
Definition: wepent.qh:6
vector angles
Definition: csprogsdefs.qc:104
void vehicles_exit(entity vehic, bool eject)
Definition: sv_vehicles.qc:788
PutClientInServer(this)
const int NUM_TEAM_1
Definition: teams.qh:18
bool autocvar_g_campaign
Definition: campaign.qh:6
float autocvar_g_balance_health_regenlinear
Definition: sv_resources.qh:27
vector lastV_angle
Definition: common.qh:62
float default_player_alpha
Definition: world.qh:66
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings...
Definition: teamplay.qc:487
float movetype
Definition: csprogsdefs.qc:98
bool SpectateSet(entity this)
Definition: client.qc:1800
int random_start_weapons_count
Number of random start weapons to give to players.
Definition: world.qh:92
float FL_NOTARGET
Definition: progsdefs.qc:238
float button15
IntrusiveList g_ladderents
Definition: ladder.qh:3
void PlayerState_detach(entity this)
Definition: state.qc:22
#define sound(e, c, s, v, a)
Definition: sound.qh:52
float button9
void VoteCount(float first_count)
Definition: vote.qc:207
const int MIN_SPEC_TIME
Definition: client.qh:399
int autocvar_g_maxplayers
Definition: client.qh:43
float autocvar_g_teamdamage_resetspeed
Definition: damage.qh:22
entity vehicle
Definition: impulse.qc:21
bool autocvar_sv_servermodelsonly
Definition: client.qh:53
bool autocvar_sv_eventlog
Definition: gamelog.qh:3
void CopyBody(entity this, float keepvelocity)
Definition: player.qc:64
ERASEABLE ACCUMULATE void Registry_send_all()
Definition: registry.qh:167
void Handicap_Initialize(entity player)
Initializes handicap to its default value.
Definition: handicap.qc:13
bool autocvar_g_playerclip_collisions
Definition: client.qh:17
#define GAMETYPE_DEFAULTED_SETTING(str)
Definition: client.qh:352
float autocvar_g_balance_armor_blockpercent
Definition: damage.qh:21
string cache_mutatormsg
Definition: world.qh:63
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition: weapon.qh:41
int oldskin
Definition: sv_monsters.qh:56
float autocvar_g_balance_armor_regen
Definition: sv_resources.qh:14
float autocvar_g_spawnshieldtime
Definition: player.qh:3
bool PlayerInList(entity player, string list)
Definition: client.qc:995
float autocvar_g_balance_health_rotstable
Definition: sv_resources.qh:31
float time
Definition: csprogsdefs.qc:16
void ClientState_detach(entity this)
Definition: state.qc:73
string gamemode_name
Definition: world.qh:47
vector velocity
Definition: csprogsdefs.qc:103
float start_armorvalue
Definition: world.qh:99
bool ClientInit_SendEntity(entity this, entity to, int sf)
Definition: client.qc:843
float DEAD_RESPAWNABLE
Definition: progsdefs.qc:277
string Campaign_GetMessage()
Definition: campaign.qc:49
ERASEABLE bool fexists(string f)
Definition: file.qh:4
#define LOG_FATAL(...)
Definition: log.qh:58
float autocvar_g_player_brightness
Definition: client.qh:19
float default_weapon_alpha
Definition: world.qh:67
float autocvar_g_balance_pause_fuel_rot_spawn
Definition: client.qh:12
float autocvar_g_respawn_ghosts_alpha
Definition: client.qh:28
float death_time
float revive_speed
Definition: damage.qh:114
ERASEABLE bool isInvisibleString(string s)
Definition: string.qh:369
void accuracy_resend(entity e)
Definition: accuracy.qc:74
void ObserverOrSpectatorThink(entity this)
Definition: client.qc:2267
#define PHYS_INPUT_BUTTON_INFO(s)
Definition: player.qh:152
bool findinlist_abbrev(string tofind, string list)
Definition: client.qc:963
float start_ammo_plasma
Definition: world.qh:89
#define FOREACH(list, cond, body)
Definition: iter.qh:19
string weaponorder_byimpulse
Definition: client.qh:60
const int FROZEN_TEMP_DYING
Definition: damage.qh:111
void ClientInit_Spawn()
Definition: client.qc:894
#define boolean(value)
Definition: bool.qh:9
#define PHYS_INPUT_BUTTON_MINIGAME(s)
Definition: player.qh:160
const int VOICETYPE_LASTATTACKER_ONLY
Definition: globalsound.qh:67
void ClientInit_misc(entity this)
Definition: client.qc:855
float DAMAGE_NO
Definition: progsdefs.qc:282
WepSet g_weaponarena_weapons
Definition: world.qh:77
float revival_time
Definition: damage.qh:113
bool server_is_dedicated
Definition: world.qh:37
void Join(entity this)
Definition: client.qc:1966
string autocvar_sv_defaultplayermodel_pink
Definition: player.qh:15
void set_movetype(entity this, int mt)
float MOVETYPE_FLY
Definition: progsdefs.qc:251
#define IS_PLAYER(v)
Definition: utils.qh:9
entity chatbubbleentity
Definition: client.qc:1207
float EF_LOWPRECISION
var void func_null()
void FixClientCvars(entity e)
Definition: client.qc:947
const int RESPAWN_SILENT
Definition: client.qh:331
void player_powerups_remove_all(entity this)
Definition: client.qc:1457
const int NUM_TEAM_3
Definition: teams.qh:20
void ClientData_Detach(entity this)
Definition: client.qc:165
float FL_CLIENT
Definition: progsdefs.qc:234
#define g_duel
Definition: duel.qh:28
const int W_TICSPERFRAME
Definition: weaponsystem.qh:14
bool SpectatePrev(entity this)
Definition: client.qc:1897
float buttonuse
Definition: dpextensions.qc:44
string cache_lastmutatormsg
Definition: world.qh:64
string autocvar_g_xonoticversion
Definition: client.qh:45
float solid
Definition: csprogsdefs.qc:99
float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
Sets the player&#39;s score to the score parameter.
Definition: scores.qc:368
void ImpulseCommands(entity this)
Definition: impulse.qc:372
float warmup_start_ammo_nails
Definition: world.qh:105
float g_weaponarena
Definition: world.qh:76
bool PlayerInIDList(entity p, string idlist)
Definition: client.qc:986
int CountSpectators(entity player, entity to)
Definition: client.qc:101
float button11
void SetNewParms()
Definition: client.qc:908