Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
client.qc
Go to the documentation of this file.
1#include "client.qh"
2
5#include <common/debug.qh>
8#include <common/ent_cs.qh>
11#include <common/items/_mod.qh>
29#include <common/net_linked.qh>
30#include <common/net_notice.qh>
33#include <common/playerstats.qh>
35#include <common/state.qh>
36#include <common/stats.qh>
39#include <common/viewloc.qh>
42#include <common/wepent.qh>
46#include <server/anticheat.qh>
47#include <server/antilag.qh>
48#include <server/bot/api.qh>
51#include <server/campaign.qh>
52#include <server/chat.qh>
53#include <server/cheats.qh>
54#include <server/clientkill.qh>
56#include <server/command/cmd.qh>
60#include <server/damage.qh>
61#include <server/gamelog.qh>
62#include <server/handicap.qh>
63#include <server/hook.qh>
64#include <server/impulse.qh>
66#include <server/ipban.qh>
67#include <server/main.qh>
69#include <server/player.qh>
70#include <server/portals.qh>
71#include <server/race.qh>
72#include <server/scores.qh>
74#include <server/spawnpoints.qh>
75#include <server/teamplay.qh>
82#include <server/world.qh>
83
84STATIC_METHOD(Client, Add, void(Client this, int _team))
85{
86 ClientConnect(this);
88 this.frame = 12; // 7
89 this.team = _team;
91}
92
93STATIC_METHOD(Client, Remove, void(Client this))
94{
95 TRANSMUTE(Observer, this);
98}
99
101{
102 if(!player) return 0; // not sure how, but best to be safe
103
104 int spec_count = 0;
105
106 FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
107 {
108 ++spec_count;
109 });
110
111 return spec_count;
112}
113
115{
116 if(!player) return; // not sure how, but best to be safe
117
118 int spec_count = 0;
119 FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
120 {
121 if(spec_count >= MAX_SPECTATORS)
122 break;
123 WriteByte(MSG_ENTITY, num_for_edict(it));
124 ++spec_count;
125 });
126}
127
128bool ClientData_Send(entity this, entity to, int sf)
129{
130 assert(to == this.owner, return false);
131
132 entity e = to;
133 if (IS_SPEC(e)) e = e.enemy;
134
135 sf = 0;
136 if (CS(e).race_completed) sf |= BIT(0); // forced scoreboard
137 if (CS(to).spectatee_status) sf |= BIT(1); // spectator ent number follows
138 if (CS(e).zoomstate) sf |= BIT(2); // zoomed
140 sf |= BIT(3); // observing blocked
142 sf |= BIT(4); // show spectators
144 sf |= (autocvar_sv_teamnagger & 0x03) << 5; // BIT(5) | BIT(6)
145 if (to.ignore_list && to.ignore_list_send_time == IGNORE_LIST_SEND_NOW && !IS_SPEC(to))
146 sf |= BIT(7); // ignore list (private, won't be sent to spectators)
147
148 WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
150
151 if (sf & BIT(1))
153
154 // spectator list is cleared on the client if this flag is not set
155 if(sf & BIT(4))
156 {
157 float specs = CountSpectators(e, to);
158 WriteByte(MSG_ENTITY, specs);
159 WriteSpectators(e, to);
160 }
161
162 // ignore list is NOT cleared on the client if this flag is not set
163 if (sf & BIT(7))
164 {
165 to.ignore_list_send_time = 0;
166 WriteString(MSG_ENTITY, strcat(to.ignore_list));
167 }
168
169 return true;
170}
171
173{
175 CS(this).clientdata.drawonlytoclient = this;
176 CS(this).clientdata.owner = this;
177}
178
180{
181 delete(CS(this).clientdata);
182 CS(this).clientdata = NULL;
183}
184
185void ClientData_Touch(entity e, bool to_spectators_too)
186{
187 entity cd = CS(e).clientdata;
188 if (cd) cd.SendFlags = 1;
189
190 if (to_spectators_too)
191 FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e,
192 {
193 entity cd_spec = CS(it).clientdata;
194 if (cd_spec) cd_spec.SendFlags = cd.SendFlags;
195 });
196}
197
198
199/*
200=============
201CheckPlayerModel
202
203Checks if the argument string can be a valid playermodel.
204Returns a valid one in doubt.
205=============
206*/
208string CheckPlayerModel(string plyermodel)
209{
210 if(FallbackPlayerModel != cvar_defstring("_cl_playermodel"))
211 {
212 // note: we cannot summon Don Strunzone here, some player may
213 // still have the model string set. In case anyone manages how
214 // to change a cvar default, we'll have a small leak here.
215 FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
216 }
217 // only in right path
218 if(substring(plyermodel, 0, 14) != "models/player/")
219 return FallbackPlayerModel;
220 // only good file extensions
221 if(substring(plyermodel, -4, 4) != ".iqm"
222 && substring(plyermodel, -4, 4) != ".zym"
223 && substring(plyermodel, -4, 4) != ".dpm"
224 && substring(plyermodel, -4, 4) != ".md3"
225 && substring(plyermodel, -4, 4) != ".psk")
226 return FallbackPlayerModel;
227
228 // forbid the LOD models
229 if(substring(plyermodel, -9, 5) == "_lod1" || substring(plyermodel, -9, 5) == "_lod2")
230 return FallbackPlayerModel;
231 if(plyermodel != strtolower(plyermodel))
232 return FallbackPlayerModel;
233 // also, restrict to server models
235 if(!fexists(plyermodel))
236 return FallbackPlayerModel;
237
238 return plyermodel;
239}
240
241void setplayermodel(entity e, string modelname)
242{
243 precache_model(modelname);
244 _setmodel(e, modelname);
248}
249
251{
254 {
255 RandomSelection_AddEnt(it, 1, 1);
256 });
258}
259
261void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
262{
263 bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this, is_forced);
264 bool recount_ready = false;
265 PlayerState_detach(this);
266
267 bool was_player = false;
268 if (IS_PLAYER(this))
269 {
270 if(GetResource(this, RES_HEALTH) >= 1)
271 {
272 // despawn effect
273 Send_Effect(EFFECT_SPAWN, this.origin, '0 0 0', 1);
274 }
275
276 // was a player, recount votes and ready status
277 if(IS_REAL_CLIENT(this))
278 {
279 if (vote_called) { VoteCount(false); }
280 this.ready = false;
281 if (warmup_stage || game_starttime > time) /* warmup OR countdown */ recount_ready = true;
282 }
284 was_player = true;
285 }
286
287 if (use_spawnpoint)
288 {
289 // first try to find a random "nice" location to view from
290 entity spot = SelectObservePoint(this);
291 bool is_observepoint = (spot != NULL);
292 if(!spot) // otherwise just use the player spawn points
293 spot = SelectSpawnPoint(this, true);
294 if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
295
296 this.angles = vec2(spot.angles);
297 // offset it so that the spectator spawns higher off the ground, looks better this way
298 setorigin(this, spot.origin + (is_observepoint ? '0 0 0' : autocvar_sv_player_viewoffset));
299 }
300 else // change origin to restore previous view origin
301 setorigin(this, this.origin + STAT(PL_VIEW_OFS, this) - STAT(PL_CROUCH_VIEW_OFS, this));
302 this.fixangle = true;
303
304 if (IS_REAL_CLIENT(this))
305 {
306 msg_entity = this;
308 WriteEntity(MSG_ONE, this);
309 }
310 // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
311 // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
313 {
314 // needed for player sounds
315 this.model = "";
316 FixPlayermodel(this);
317 }
318 setmodel(this, MDL_Null);
319 setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
320 this.view_ofs = '0 0 0';
321
323 Portal_ClearAll(this);
324 SetSpectatee(this, NULL);
325
326 if (this.alivetime_start)
327 {
328 if (!warmup_stage)
330 this.alivetime_start = 0;
331 }
332
333 if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
334
335 TRANSMUTE(Observer, this);
336
338 accuracy_resend(this);
339
341 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
342
343 CS(this).spectatortime = time;
344 if (!autocvar_sv_spectate && CS(this).autojoin_checked) // unnecessary if autojoin succeeds, on failure it notifies
345 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
346
347 if(this.bot_attack)
349 this.bot_attack = false;
350 if(this.monster_attack)
352 this.monster_attack = false;
353 STAT(HUD, this) = HUD_NORMAL;
354 this.iscreature = false;
356 if(this.damagedbycontents)
358 this.damagedbycontents = false;
359 SetResourceExplicit(this, RES_HEALTH, FRAGS_SPECTATOR);
360 SetSpectatee_status(this, etof(this));
361 this.takedamage = DAMAGE_NO;
362 this.solid = SOLID_NOT;
363 set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink
364 this.flags = FL_CLIENT | FL_NOTARGET;
365 this.effects = 0;
367 this.pauserotarmor_finished = 0;
369 this.pauseregen_finished = 0;
370 this.damageforcescale = 0;
371 this.death_time = 0;
372 this.respawn_flags = 0;
373 this.respawn_time = 0;
374 STAT(RESPAWN_TIME, this) = 0;
375 this.alpha = 0;
376 this.scale = 0;
377 this.fade_time = 0;
378 this.pain_finished = 0;
379 STAT(AIR_FINISHED, this) = 0;
380 //this.dphitcontentsmask = 0;
384 this.pushltime = 0;
385 this.istypefrag = 0;
386 setthink(this, func_null);
387 this.nextthink = 0;
388 this.deadflag = DEAD_NO;
389 UNSET_DUCKED(this);
390 this.draggable = drag_undraggable;
391
392 player_powerups_remove_all(this, was_player);
393 this.items = 0;
394 STAT(WEAPONS, this) = '0 0 0';
395 this.drawonlytoclient = this;
396
397 this.viewloc = NULL;
398
399 //this.spawnpoint_targ = NULL; // keep it so they can return to where they were?
400
401 this.weaponmodel = "";
402 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
403 {
404 this.weaponentities[slot] = NULL;
405 }
407 CS(this).killcount = FRAGS_SPECTATOR;
408 this.velocity = '0 0 0';
409 this.avelocity = '0 0 0';
410 this.punchangle = '0 0 0';
411 this.punchvector = '0 0 0';
412 this.oldvelocity = this.velocity;
413 this.event_damage = func_null;
414 this.event_heal = func_null;
415
416 for(int slot = 0; slot < MAX_AXH; ++slot)
417 {
418 entity axh = this.(AuxiliaryXhair[slot]);
419 this.(AuxiliaryXhair[slot]) = NULL;
420
421 if(axh.owner == this && axh != NULL && !wasfreed(axh))
422 delete(axh);
423 }
424
425 if (mutator_returnvalue)
426 {
427 // mutator prevents resetting teams+score
428 }
429 else
430 {
431 SetPlayerTeam(this, -1, TEAM_CHANGE_SPECTATOR); // clears scores too in gametypes without teams
432 this.frags = FRAGS_SPECTATOR;
433 }
434
436
437 if (CS(this).just_joined)
438 CS(this).just_joined = false;
439
440 if (recount_ready)
441 ReadyCount(); // must be called after SetPlayerTeam() and TRANSMUTE(Observer
442}
443
445{
446 get_model_parameters(this.model, this.skin);
449 if (s < 0) return SPECIES_HUMAN;
450 return s;
451}
452
455{
456 string defaultmodel = "";
457 int defaultskin = 0;
459 {
460 if(teamplay)
461 {
462 switch(player.team)
463 {
468 }
469 }
470
471 if(defaultmodel == "")
472 {
473 defaultmodel = autocvar_sv_defaultplayermodel;
474 defaultskin = autocvar_sv_defaultplayerskin;
475 }
476
477 int n = tokenize_console(defaultmodel);
478 if(n > 0)
479 {
480 defaultmodel = argv(floor(n * CS(player).model_randomizer));
481 // However, do NOT randomize if the player-selected model is in the list.
482 for (int i = 0; i < n; ++i)
483 if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin))
484 defaultmodel = argv(i);
485 }
486
487 int i = strstrofs(defaultmodel, ":", 0);
488 if(i >= 0)
489 {
490 defaultskin = stof(substring(defaultmodel, i+1, -1));
491 defaultmodel = substring(defaultmodel, 0, i);
492 }
493 }
494 if(autocvar_sv_defaultcharacterskin && !defaultskin)
495 {
496 if(teamplay)
497 {
498 switch(player.team)
499 {
500 case NUM_TEAM_1: defaultskin = autocvar_sv_defaultplayerskin_red; break;
501 case NUM_TEAM_2: defaultskin = autocvar_sv_defaultplayerskin_blue; break;
502 case NUM_TEAM_3: defaultskin = autocvar_sv_defaultplayerskin_yellow; break;
503 case NUM_TEAM_4: defaultskin = autocvar_sv_defaultplayerskin_pink; break;
504 }
505 }
506
507 if(!defaultskin)
508 defaultskin = autocvar_sv_defaultplayerskin;
509 }
510
511 MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player);
512 defaultmodel = M_ARGV(0, string);
513 defaultskin = M_ARGV(1, int);
514
515 bool chmdl = false;
516 int oldskin;
517 if(defaultmodel != "")
518 {
519 if (defaultmodel != player.model)
520 {
521 vector m1 = player.mins;
522 vector m2 = player.maxs;
523 setplayermodel (player, defaultmodel);
524 setsize (player, m1, m2);
525 chmdl = true;
526 }
527
528 oldskin = player.skin;
529 player.skin = defaultskin;
530 }
531 else
532 {
533 if (player.playermodel != player.model || player.playermodel == "")
534 {
535 player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop
536 vector m1 = player.mins;
537 vector m2 = player.maxs;
538 setplayermodel (player, player.playermodel);
539 setsize (player, m1, m2);
540 chmdl = true;
541 }
542
544 {
545 oldskin = player.skin;
546 player.skin = stof(player.playerskin);
547 }
548 else
549 {
550 oldskin = player.skin;
551 player.skin = defaultskin;
552 }
553 }
554
555 if(chmdl || oldskin != player.skin) // model or skin has changed
556 {
557 player.species = player_getspecies(player); // update species
559 UpdatePlayerSounds(player); // update skin sounds
560 }
561
562 if(!teamplay)
564 if(player.clientcolors != stof(autocvar_sv_defaultplayercolors))
566}
567
569{
570 SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
571 SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
572 SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
573 SetResource(this, RES_CELLS, warmup_start_ammo_cells);
574 SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
575 SetResource(this, RES_HEALTH, warmup_start_health);
577 STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
578}
579
581{
582 if (MUTATOR_CALLHOOK(ForbidSpawn, this))
583 return;
584
585 if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
586
587 PlayerState_attach(this);
588 accuracy_resend(this);
589
590 if (teamplay)
591 {
592 if (this.bot_forced_team)
594 else if (this.team <= 0)
595 {
596 if (this.wants_join > 0)
598 else
600 }
601 }
602
603 entity spot = SelectSpawnPoint(this, false);
604 if (!spot)
605 {
606 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
607 return; // spawn failed
608 }
609
610 TRANSMUTE(Player, this);
611
612 this.iscreature = true;
614 if(!this.damagedbycontents)
616 this.damagedbycontents = true;
618 this.solid = SOLID_SLIDEBOX;
624 this.frags = FRAGS_PLAYER;
628 this.flags |= FL_NOTARGET;
629 this.takedamage = DAMAGE_AIM;
631
632 if (warmup_stage)
634 else
635 {
636 SetResource(this, RES_SHELLS, start_ammo_shells);
637 SetResource(this, RES_BULLETS, start_ammo_nails);
638 SetResource(this, RES_ROCKETS, start_ammo_rockets);
639 SetResource(this, RES_CELLS, start_ammo_cells);
640 SetResource(this, RES_FUEL, start_ammo_fuel);
641 SetResource(this, RES_HEALTH, start_health);
643 STAT(WEAPONS, this) = start_weapons;
644 if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
645 {
648 }
649 }
650 SetSpectatee_status(this, 0);
651
652 PS(this).dual_weapons = '0 0 0';
653
654 if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
655 StatusEffects_apply(STATUSEFFECT_Superweapon, this, time + autocvar_g_balance_superweapons_time, 0);
656
657 this.items = start_items;
658
659 float shieldtime = time + autocvar_g_spawnshieldtime;
660
666 {
667 float f = game_starttime - time;
668 shieldtime += f;
669 this.pauserotarmor_finished += f;
670 this.pauserothealth_finished += f;
671 this.pauseregen_finished += f;
672 }
673
674 StatusEffects_apply(STATUSEFFECT_SpawnShield, this, shieldtime, 0);
675
677 this.death_time = 0;
678 this.respawn_flags = 0;
679 this.respawn_time = 0;
680 STAT(RESPAWN_TIME, this) = 0;
682 ? 0.8125 // DP model scaling uses 1/16 accuracy and 13/16 is closest to 56/69
684 this.fade_time = 0;
685 this.pain_finished = 0;
686 this.pushltime = 0;
687 setthink(this, func_null); // players have no think function
688 this.nextthink = 0;
689 this.dmg_team = 0;
690 this.spawn_time = time;
691
692 PS(this).ballistics_density = autocvar_g_ballistics_density_player;
693
694 this.deadflag = DEAD_NO;
695
696 this.angles = spot.angles;
697 this.angles_z = 0; // never spawn tilted even if the spot says to
698 if (IS_BOT_CLIENT(this))
699 {
700 this.v_angle = this.angles;
701 bot_aim_reset(this);
702 }
703 this.fixangle = true; // turn this way immediately
704 this.oldvelocity = this.velocity = '0 0 0';
705 this.avelocity = '0 0 0';
706 this.punchangle = '0 0 0';
707 this.punchvector = '0 0 0';
708
709 STAT(AIR_FINISHED, this) = 0;
712
713 entity spawnevent = new_pure(spawnevent);
714 spawnevent.owner = this;
715 Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
716
717 // Cut off any still running player sounds.
719
720 this.model = "";
721 FixPlayermodel(this);
722 this.drawonlytoclient = NULL;
723
724 this.viewloc = NULL;
725
726 for(int slot = 0; slot < MAX_AXH; ++slot)
727 {
728 entity axh = this.(AuxiliaryXhair[slot]);
729 this.(AuxiliaryXhair[slot]) = NULL;
730
731 if(axh.owner == this && axh != NULL && !wasfreed(axh))
732 delete(axh);
733 }
734
735 this.spawnpoint_targ = NULL;
736
737 UNSET_DUCKED(this);
738 this.view_ofs = STAT(PL_VIEW_OFS, this);
739 setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
740 this.spawnorigin = spot.origin;
741 setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
742 // don't reset back to last position, even if new position is stuck in solid
743 this.oldorigin = this.origin;
744 if(this.conveyor)
745 IL_REMOVE(g_conveyed, this);
746 this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
747 if(this.swampslug)
748 IL_REMOVE(g_swamped, this);
749 this.swampslug = NULL;
750 this.swamp_interval = 0;
751 if(this.ladder_entity)
752 IL_REMOVE(g_ladderents, this);
753 this.ladder_entity = NULL;
754 IL_EACH(g_counters, it.realowner == this,
755 {
756 delete(it);
757 });
758 STAT(HUD, this) = HUD_NORMAL;
759
760 this.event_damage = PlayerDamage;
761 this.event_heal = PlayerHeal;
762
763 this.draggable = func_null;
764
765 if(!this.bot_attack)
766 IL_PUSH(g_bot_targets, this);
767 this.bot_attack = true;
768 if(!this.monster_attack)
770 this.monster_attack = true;
771 navigation_dynamicgoal_init(this, false);
772
774
775 // player was spectator
776 if (CS(this).killcount == FRAGS_SPECTATOR)
777 {
778 PlayerScore_Clear(this);
779 CS(this).killcount = 0;
780 CS(this).startplaytime = time;
781 }
782
783 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
784 {
785 .entity weaponentity = weaponentities[slot];
786 CL_SpawnWeaponentity(this, weaponentity);
787 }
789 this.colormod = '1 1 1' * autocvar_g_player_brightness;
791
792 this.speedrunning = false;
793
794 this.counter_cnt = 0;
795 this.fragsfilter_cnt = 0;
796
798
799 // reset fields the weapons may use
800 FOREACH(Weapons, true,
801 {
802 it.wr_resetplayer(it, this);
803 // reload all reloadable weapons
804 if (it.spawnflags & WEP_FLAG_RELOADABLE)
805 {
806 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
807 {
808 .entity weaponentity = weaponentities[slot];
809 this.(weaponentity).weapon_load[it.m_id] = it.reloading_ammo;
810 }
811 }
812 });
813
814 MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
815 {
816 string s = spot.target;
817 if(g_assault || g_race) // TODO: make targeting work in assault & race without this hack
818 spot.target = string_null;
819 SUB_UseTargets(spot, this, NULL);
820 if(g_assault || g_race)
821 spot.target = s;
822 }
823
825 {
826 sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n"));
827 delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
828 }
829
830 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
831 {
832 .entity weaponentity = weaponentities[slot];
833 entity w_ent = this.(weaponentity);
834 if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
835 w_ent.m_switchweapon = w_getbestweapon(this, weaponentity);
836 else
837 w_ent.m_switchweapon = WEP_Null;
838 w_ent.m_weapon = WEP_Null;
839 w_ent.weaponname = "";
840 w_ent.m_switchingweapon = WEP_Null;
841 w_ent.cnt = -1;
842 }
843
844 MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
845
846 if (CS(this).impulse) ImpulseCommands(this);
847
848 W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
849 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
850 {
851 .entity weaponentity = weaponentities[slot];
852 W_WeaponFrame(this, weaponentity);
853 }
854
855 if (!warmup_stage && !this.alivetime_start)
857
858 antilag_clear(this, CS(this));
859
860 if (warmup_stage)
861 ReadyCount();
862}
863
866{
867 if (IS_REAL_CLIENT(this))
868 {
869 msg_entity = this;
871 WriteEntity(MSG_ONE, this);
872 }
873 if (game_stopped)
874 TRANSMUTE(Observer, this);
875
876 bool use_spawnpoint = (!this.enemy); // check this.enemy here since SetSpectatee will clear it
877 SetSpectatee(this, NULL);
878
879 // reset player keys
880 if(PS(this))
881 PS(this).itemkeys = 0;
882
884
885 if (IS_OBSERVER(this))
886 PutObserverInServer(this, false, use_spawnpoint);
887 else if (IS_PLAYER(this))
888 PutPlayerInServer(this);
889
891}
892
893// TODO do we need all these fields, or should we stop autodetecting runtime
894// changes and just have a console command to update this?
895bool ClientInit_SendEntity(entity this, entity to, int sf)
896{
897 WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
898 return = true;
899 msg_entity = to;
900 // MSG_INIT replacement
901 // TODO: make easier to use
904 ClientInit_misc(this);
905 MUTATOR_CALLHOOK(Ent_Init);
906}
908{
909 int channel = MSG_ONE;
910 WriteHeader(channel, ENT_CLIENT_INIT);
911 WriteByte(channel, g_nexball_meter_period * 32);
912 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
913 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
914 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
915 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
916 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
917 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
918 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
919 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
920
921 if(autocvar_sv_foginterval && world.fog != "")
922 WriteString(channel, world.fog);
923 else
924 WriteString(channel, "");
925 WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
926 WriteByte(channel, this.cnt * 255.0); // g_balance_damagepush_speedfactor
927 WriteByte(channel, serverflags);
929}
930
932{
933 this.nextthink = time;
935 {
937 this.SendFlags |= 1;
938 }
940 {
942 this.SendFlags |= 1;
943 }
944}
945
947{
948 entity e = new_pure(clientinit);
951
953}
954
955/*
956=============
957SetNewParms
958=============
959*/
961{
962 // initialize parms for a new player
963 parm1 = -(86400 * 366);
964
966}
967
968/*
969=============
970SetChangeParms
971=============
972*/
974{
975 // save parms for level change
976 parm1 = CS(this).parm_idlesince - time;
977
979}
980
981/*
982=============
983DecodeLevelParms
984=============
985*/
987{
988 // load parms
989 CS(this).parm_idlesince = parm1;
990 if (CS(this).parm_idlesince == -(86400 * 366))
991 CS(this).parm_idlesince = time;
992
993 // whatever happens, allow 60 seconds of idling directly after connect for map loading
994 CS(this).parm_idlesince = max(CS(this).parm_idlesince, time - autocvar_sv_maxidle + 60);
995
997}
998
1000{
1001 // send prediction settings to the client
1002 if(autocvar_g_antilag == 3) // client side hitscan
1003 stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
1004 if(autocvar_sv_gentle)
1005 stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
1006
1007 stuffcmd(e, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
1008 stuffcmd(e, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
1009
1010 stuffcmd(e, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin));
1011
1013}
1014
1015bool findinlist_abbrev(string tofind, string list)
1016{
1017 if(list == "" || tofind == "")
1018 return false; // empty list or search, just return
1019
1020 // this function allows abbreviated strings!
1021 FOREACH_WORD(list, it != "" && it == substring(tofind, 0, strlen(it)),
1022 {
1023 return true;
1024 });
1025
1026 return false;
1027}
1028
1029bool PlayerInIPList(entity p, string iplist)
1030{
1031 // some safety checks (never allow local?)
1032 if(p.netaddress == "local" || p.netaddress == "" || !IS_REAL_CLIENT(p))
1033 return false;
1034
1035 return findinlist_abbrev(p.netaddress, iplist);
1036}
1037
1038bool PlayerInIDList(entity p, string idlist)
1039{
1040 // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this
1041 if(!p.crypto_idfp)
1042 return false;
1043
1044 return findinlist_abbrev(p.crypto_idfp, idlist);
1045}
1046
1047bool PlayerInList(entity player, string list)
1048{
1049 if (list == "")
1050 return false;
1051 return boolean(PlayerInIDList(player, list) || PlayerInIPList(player, list));
1052}
1053
1054#ifdef DP_EXT_PRECONNECT
1055/*
1056=============
1057ClientPreConnect
1058
1059Called once (not at each match start) when a client begins a connection to the server
1060=============
1061*/
1062void ClientPreConnect(entity this)
1063{
1065 {
1066 GameLogEcho(sprintf(":connect:%d:%d:%s",
1067 this.playerid,
1068 etof(this),
1069 IS_REAL_CLIENT(this) ? this.netaddress : "bot"));
1070 }
1071}
1072#endif
1073
1074// NOTE csqc uses the active mutators list sent by this function
1075// to understand which mutators are enabled
1076// also note that they aren't all registered mutators, e.g. jetpack, low gravity
1077void SendWelcomeMessage(entity this, int msg_type)
1078{
1079 if (boolean(autocvar_g_campaign))
1080 {
1081 WriteByte(msg_type, 1);
1082 WriteByte(msg_type, Campaign_GetLevelNum());
1083 return;
1084 }
1085
1086 int flags = 0;
1087 if (CS(this).version_mismatch)
1088 flags |= 2;
1089 if (CS(this).version < autocvar_gameversion)
1090 flags |= 4;
1092 if (MapInfo_Map_author != "")
1093 flags |= 8;
1094 WriteByte(msg_type, flags);
1095
1096 WriteString(msg_type, autocvar_hostname);
1098
1100 if (flags & 8)
1103
1105 WriteByte(msg_type, GetPlayerLimit());
1106
1107 MUTATOR_CALLHOOK(BuildMutatorsPrettyString, "");
1108 string modifications = M_ARGV(0, string);
1109
1110 if (!g_weaponarena && cvar("g_balance_blaster_weaponstartoverride") == 0)
1111 modifications = strcat(modifications, ", No start weapons");
1112 if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
1113 modifications = strcat(modifications, ", Low gravity");
1114 if(g_weapon_stay && !g_cts)
1115 modifications = strcat(modifications, ", Weapons stay");
1117 modifications = strcat(modifications, ", Jetpack");
1118 modifications = substring(modifications, 2, strlen(modifications) - 2);
1119
1120 WriteString(msg_type, modifications);
1121
1123
1125 {
1128 }
1129
1130 WriteString(msg_type, cache_mutatormsg);
1131
1132 WriteString(msg_type, strreplace("\\n", "\n", autocvar_sv_motd));
1133}
1134
1144{
1145 if (Ban_MaybeEnforceBanOnce(this)) return;
1146 assert(!IS_CLIENT(this), return);
1147 this.flags |= FL_CLIENT;
1148 assert(player_count >= 0, player_count = 0);
1149
1150 TRANSMUTE(Client, this);
1151 CS(this).version_nagtime = time + 10 + random() * 10;
1152
1153 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname);
1154
1155 bot_clientconnect(this);
1156
1157 // after bot_clientconnect
1158 if (teamplay)
1159 this.clientcolors = 0;
1160
1161 this.team = -1;
1163
1164 TRANSMUTE(Observer, this);
1165
1166 PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid));
1167
1168 // always track bots, don't ask for cl_allow_uidtracking
1169 if (IS_BOT_CLIENT(this))
1171 else
1172 CS(this).allowed_timeouts = autocvar_sv_timeout_number;
1173
1175 GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false)));
1176
1177 CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects
1178 this.wants_join = 0;
1179
1180 stuffcmd(this, clientstuff, "\n");
1181 stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
1182
1183 FixClientCvars(this);
1184
1185 // get version info from player
1186 stuffcmd(this, "cmd clientversion $gameversion\n");
1187
1188 // notify about available teams
1189 if (teamplay)
1190 {
1191 entity balance = TeamBalance_CheckAllowedTeams(this);
1192 int t = TeamBalance_GetAllowedTeams(balance);
1193 TeamBalance_Destroy(balance);
1194 stuffcmd(this, sprintf("set _teams_available %d\n", t));
1195 }
1196 else
1197 {
1198 stuffcmd(this, "set _teams_available 0\n");
1199 }
1200
1202
1203 CS(this).spectatortime = time;
1204 CS(this).jointime = time;
1205
1206 if (IS_REAL_CLIENT(this))
1207 {
1209
1210 if (g_weaponarena_weapons == WEPSET(TUBA))
1211 stuffcmd(this, "cl_cmd settemp chase_active 1\n");
1212 // quickmenu file must be put in a subfolder with an unique name
1213 // to reduce chances of overriding custom client quickmenus
1215 stuffcmd(this, sprintf("cl_cmd settemp _hud_panel_quickmenu_file_from_server %s\n", "wpeditor.txt"));
1217 stuffcmd(this, sprintf("cl_cmd settemp _hud_panel_quickmenu_file_from_server %s\n", autocvar_sv_quickmenu_file));
1218 }
1219
1220 if (!autocvar_sv_foginterval && world.fog != "")
1221 stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
1222
1223 CSQCMODEL_AUTOINIT(this);
1224
1225 CS(this).model_randomizer = random();
1226
1227 if (IS_REAL_CLIENT(this))
1228 sv_notice_join(this);
1229
1230 this.move_qcphysics = true;
1231
1232 // update physics stats (players can spawn before physics runs)
1233 Physics_UpdateStats(this);
1234
1235 IL_EACH(g_initforplayer, it.init_for_player,
1236 {
1237 it.init_for_player(it, this);
1238 });
1239
1240 Handicap_Initialize(this);
1241
1242 // playban
1244 TRANSMUTE(Observer, this);
1245
1246 if (PlayerInList(this, autocvar_g_chatban_list)) // chatban
1247 CS(this).muted = true;
1248
1250
1251 if (player_count == 1)
1252 {
1254 setpause(0);
1255 localcmd("\nsv_hook_firstjoin\n");
1256 }
1257
1258 if (get_nextmap() != "")
1260
1261 // cvar hook/callback TODO: recheck this when the cvar is changed
1264 this.vote_master = true;
1265}
1266
1269void player_powerups_remove_all(entity this, bool allow_poweroff_sound);
1270
1271/*
1272=============
1273ClientDisconnect
1274
1275Called when a client disconnects from the server
1276=============
1277*/
1279{
1280 assert(IS_CLIENT(this), return);
1281
1282 strfree(this.ignore_list);
1284 {
1285 // don't remove permanent ignores from db, only from list
1286 ignore_remove_player(it, this, false);
1287 });
1288
1290 if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
1291 if (CS(this).active_minigame) part_minigame(this);
1292 if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN, this.origin, '0 0 0', 1);
1293
1295 GameLogEcho(strcat(":part:", ftos(this.playerid)));
1296
1297 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
1298
1299 if(IS_SPEC(this))
1300 SetSpectatee(this, NULL);
1301
1303
1304 strfree(CS(this).netname_previous); // needs to be before the CS entity is removed!
1306 ClientState_detach(this);
1307
1308 Portal_ClearAll(this);
1309
1311
1313
1314 // Here, everything has been done that requires this player to be a client.
1315
1316 this.flags &= ~FL_CLIENT;
1317
1318 if (this.chatbubbleentity) delete(this.chatbubbleentity);
1319 if (this.killindicator) delete(this.killindicator);
1320
1321 IL_EACH(g_counters, it.realowner == this,
1322 {
1323 delete(it);
1324 });
1325
1327
1329
1330 strfree(this.clientstatus);
1331 if (this.personal) delete(this.personal);
1332
1333 this.playerid = 0;
1334 if (warmup_stage || game_starttime > time) /* warmup OR countdown */ ReadyCount();
1335 if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
1336
1337 player_powerups_remove_all(this, IS_PLAYER(this)); // stop powerup sound
1338
1339 ONREMOVE(this);
1340
1341 if (player_count == 0)
1342 localcmd("\nsv_hook_lastleave\n");
1343
1348}
1349
1351{
1352 this.nextthink = time;
1353
1354 entity ownr = this.owner;
1355 if (ownr.alpha < 0 || ownr.chatbubbleentity != this)
1356 {
1357 if (ownr) // but why can that ever be NULL?
1358 ownr.chatbubbleentity = NULL;
1359 delete(this);
1360 return;
1361 }
1362
1363 this.mdl = "";
1364
1365 if (!IS_DEAD(ownr) && IS_PLAYER(ownr) && !MUTATOR_CALLHOOK(ShowChatBubble, ownr, this))
1366 {
1368 this.mdl = "models/sprites/minigame_busy.iqm";
1369 else if (PHYS_INPUT_BUTTON_CHAT(ownr))
1370 this.mdl = "models/misc/chatbubble.spr";
1371 }
1372
1373 if (this.model != this.mdl)
1374 _setmodel(this, this.mdl);
1375}
1376
1378{
1379 if (this.alpha < 0)
1380 return;
1381 // spawn a chatbubble entity if needed
1382 if (!this.chatbubbleentity)
1383 {
1385 this.chatbubbleentity.owner = this;
1386 this.chatbubbleentity.exteriormodeltoclient = this;
1388 this.chatbubbleentity.nextthink = time;
1389 setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
1390 //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
1391 setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1');
1392 setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth
1393 this.chatbubbleentity.mdl = this.chatbubbleentity.model;
1394 //this.chatbubbleentity.model = "";
1395 this.chatbubbleentity.effects = EF_LOWPRECISION;
1396 }
1397}
1398
1400{
1401 if(MUTATOR_CALLHOOK(CalculateRespawnTime, this))
1402 return;
1403
1404 float gametype_setting_tmp;
1405 float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
1406 float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
1407 float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large);
1408 float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count);
1409 float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count);
1410 float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves);
1411
1412 float pcount = 1; // Include myself whether or not team is already set right and I'm a "player".
1413 if (teamplay)
1414 {
1415 FOREACH_CLIENT(IS_PLAYER(it) && it != this,
1416 {
1417 if(it.team == this.team)
1418 ++pcount;
1419 });
1420 if (sdelay_small_count == 0)
1421 sdelay_small_count = 1;
1422 if (sdelay_large_count == 0)
1423 sdelay_large_count = 1;
1424 }
1425 else
1426 {
1427 FOREACH_CLIENT(IS_PLAYER(it) && it != this,
1428 {
1429 ++pcount;
1430 });
1431 if (sdelay_small_count == 0)
1432 {
1433 if (IS_INDEPENDENT_PLAYER(this))
1434 {
1435 // Players play independently. No point in requiring enemies.
1436 sdelay_small_count = 1;
1437 }
1438 else
1439 {
1440 // Players play AGAINST each other. Enemies required.
1441 sdelay_small_count = 2;
1442 }
1443 }
1444 if (sdelay_large_count == 0)
1445 {
1446 if (IS_INDEPENDENT_PLAYER(this))
1447 {
1448 // Players play independently. No point in requiring enemies.
1449 sdelay_large_count = 1;
1450 }
1451 else
1452 {
1453 // Players play AGAINST each other. Enemies required.
1454 sdelay_large_count = 2;
1455 }
1456 }
1457 }
1458
1459 float sdelay;
1460
1461 if (pcount <= sdelay_small_count)
1462 sdelay = sdelay_small;
1463 else if (pcount >= sdelay_large_count)
1464 sdelay = sdelay_large;
1465 else // NOTE: this case implies sdelay_large_count > sdelay_small_count.
1466 sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count);
1467
1468 if(waves)
1469 this.respawn_time = ceil((time + sdelay) / waves) * waves;
1470 else
1471 this.respawn_time = time + sdelay;
1472
1473 if(sdelay < sdelay_max)
1474 this.respawn_time_max = time + sdelay_max;
1475 else
1476 this.respawn_time_max = this.respawn_time;
1477
1478 if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75))
1479 this.respawn_countdown = 10; // first number to count down from is 10
1480 else
1481 this.respawn_countdown = -1; // do not count down
1482
1485}
1486
1487// LordHavoc: this hack will be removed when proper _pants/_shirt layers are
1488// added to the model skins
1489/*void UpdateColorModHack()
1490{
1491 float c = this.clientcolors & 15;
1492 // LordHavoc: only bothering to support white, green, red, yellow, blue
1493 if (!teamplay) this.colormod = '0 0 0';
1494 else if (c == 0) this.colormod = '1.00 1.00 1.00';
1495 else if (c == 3) this.colormod = '0.10 1.73 0.10';
1496 else if (c == 4) this.colormod = '1.73 0.10 0.10';
1497 else if (c == 12) this.colormod = '1.22 1.22 0.10';
1498 else if (c == 13) this.colormod = '0.10 0.10 1.73';
1499 else this.colormod = '1 1 1';
1500}*/
1501
1502void respawn(entity this)
1503{
1504 bool damagedbycontents_prev = this.damagedbycontents;
1505 if(this.alpha >= 0)
1506 {
1508 {
1509 this.solid = SOLID_NOT;
1510 this.takedamage = DAMAGE_NO;
1511 this.damagedbycontents = false;
1517 Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1);
1520 }
1521 else
1522 SUB_SetFade (this, time, 1); // fade out the corpse immediately
1523 }
1524
1525 CopyBody(this, 1);
1526 this.damagedbycontents = damagedbycontents_prev;
1527
1528 this.effects |= EF_NODRAW; // prevent another CopyBody
1529 PutClientInServer(this);
1530}
1531
1532void play_countdown(entity this, float finished, Sound samp)
1533{
1534 TC(Sound, samp);
1535 float time_left = finished - time;
1536 if(IS_REAL_CLIENT(this) && time_left < 6 && floor(time_left - frametime) != floor(time_left))
1537 sound(this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
1538}
1539
1540// it removes special powerups not handled by StatusEffects
1541void player_powerups_remove_all(entity this, bool allow_poweroff_sound)
1542{
1544 {
1545 // don't play the poweroff sound when the game restarts or the player disconnects
1546 if (allow_poweroff_sound && time > game_starttime + 1 && IS_CLIENT(this)
1548 sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
1549
1551 stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
1552
1554 }
1555}
1556
1558{
1559 if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !game_stopped)
1560 this.modelflags |= MF_ROCKET;
1561 else
1562 this.modelflags &= ~MF_ROCKET;
1563
1564 this.effects &= ~EF_NODEPTHTEST;
1565
1566 if (IS_DEAD(this))
1567 player_powerups_remove_all(this, true);
1568
1569 if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
1570 return;
1571
1572 // add a way to see what the items were BEFORE all of these checks for the mutator hook
1573 int items_prev = this.items;
1574
1575 if (!MUTATOR_IS_ENABLED(mutator_instagib))
1576 {
1577 // NOTE: superweapons are a special case and as such are handled here instead of the status effects system
1578 if (this.items & IT_SUPERWEAPON)
1579 {
1580 if (!(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS))
1581 {
1582 StatusEffects_remove(STATUSEFFECT_Superweapon, this, STATUSEFFECT_REMOVE_NORMAL);
1583 this.items &= ~IT_SUPERWEAPON;
1584 //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
1585 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
1586 }
1587 else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
1588 {
1589 // don't let them run out
1590 }
1591 else
1592 {
1593 play_countdown(this, StatusEffects_gettime(STATUSEFFECT_Superweapon, this), SND_POWEROFF);
1594 if (time >= StatusEffects_gettime(STATUSEFFECT_Superweapon, this))
1595 {
1596 this.items &= ~IT_SUPERWEAPON;
1597 STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
1598 //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
1599 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
1600 }
1601 }
1602 }
1603 else if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
1604 {
1605 if (time < StatusEffects_gettime(STATUSEFFECT_Superweapon, this) || (this.items & IT_UNLIMITED_SUPERWEAPONS))
1606 {
1607 this.items |= IT_SUPERWEAPON;
1608 if(!(this.items & IT_UNLIMITED_SUPERWEAPONS))
1609 {
1610 if(!g_cts)
1611 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
1612 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
1613 }
1614 }
1615 else
1616 {
1617 if(StatusEffects_active(STATUSEFFECT_Superweapon, this))
1618 StatusEffects_remove(STATUSEFFECT_Superweapon, this, STATUSEFFECT_REMOVE_TIMEOUT);
1619 STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
1620 }
1621 }
1622 else if(StatusEffects_active(STATUSEFFECT_Superweapon, this)) // cheaper to check than to update each frame!
1623 {
1624 StatusEffects_remove(STATUSEFFECT_Superweapon, this, STATUSEFFECT_REMOVE_CLEAR);
1625 }
1626 }
1627
1629 this.effects |= EF_NODEPTHTEST;
1630
1632 this.effects |= EF_FULLBRIGHT;
1633
1634 MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
1635}
1636
1637float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
1638{
1639 if(current > stable)
1640 return current;
1641 else if(current > stable - 0.25) // when close enough, "snap"
1642 return stable;
1643 else
1644 return min(stable, current + (stable - current) * regenfactor * regenframetime);
1645}
1646
1647float CalcRot(float current, float stable, float rotfactor, float rotframetime)
1648{
1649 if(current < stable)
1650 return current;
1651 else if(current < stable + 0.25) // when close enough, "snap"
1652 return stable;
1653 else
1654 return max(stable, current + (stable - current) * rotfactor * rotframetime);
1655}
1656
1657void RotRegen(entity this, Resource res, float limit_mod,
1658 float regenstable, float regenfactor, float regenlinear, float regenframetime,
1659 float rotstable, float rotfactor, float rotlinear, float rotframetime)
1660{
1661 float old = GetResource(this, res);
1662 float current = old;
1663 if(current > rotstable)
1664 {
1665 if(rotframetime > 0)
1666 {
1667 current = CalcRot(current, rotstable, rotfactor, rotframetime);
1668 current = max(rotstable, current - rotlinear * rotframetime);
1669 }
1670 }
1671 else if(current < regenstable)
1672 {
1673 if(regenframetime > 0)
1674 {
1675 current = CalcRegen(current, regenstable, regenfactor, regenframetime);
1676 current = min(regenstable, current + regenlinear * regenframetime);
1677 }
1678 }
1679
1680 float limit = GetResourceLimit(this, res) * limit_mod;
1681 if(current > limit)
1682 current = limit;
1683
1684 if (current != old)
1685 SetResource(this, res, current);
1686}
1687
1689{
1690 float max_mod, regen_mod, rot_mod, limit_mod;
1691 max_mod = regen_mod = rot_mod = limit_mod = 1;
1692
1693 float regen_health = autocvar_g_balance_health_regen;
1694 float regen_health_linear = autocvar_g_balance_health_regenlinear;
1695 float regen_health_rot = autocvar_g_balance_health_rot;
1696 float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
1697 float regen_health_stable = autocvar_g_balance_health_regenstable;
1698 float regen_health_rotstable = autocvar_g_balance_health_rotstable;
1699 bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
1700 regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
1701 max_mod = M_ARGV(1, float);
1702 regen_mod = M_ARGV(2, float);
1703 rot_mod = M_ARGV(3, float);
1704 limit_mod = M_ARGV(4, float);
1705 regen_health = M_ARGV(5, float);
1706 regen_health_linear = M_ARGV(6, float);
1707 regen_health_rot = M_ARGV(7, float);
1708 regen_health_rotlinear = M_ARGV(8, float);
1709 regen_health_stable = M_ARGV(9, float);
1710 regen_health_rotstable = M_ARGV(10, float);
1711
1712 float rotstable, regenstable, rotframetime, regenframetime;
1713
1714 if(!mutator_returnvalue)
1715 {
1718 regenframetime = (time > this.pauseregen_finished) ? (regen_mod * frametime) : 0;
1719 rotframetime = (time > this.pauserotarmor_finished) ? (rot_mod * frametime) : 0;
1720 RotRegen(this, RES_ARMOR, limit_mod,
1723
1724 // NOTE: max_mod is only applied to health
1725 regenstable = regen_health_stable * max_mod;
1726 rotstable = regen_health_rotstable * max_mod;
1727 regenframetime = (time > this.pauseregen_finished) ? (regen_mod * frametime) : 0;
1728 rotframetime = (time > this.pauserothealth_finished) ? (rot_mod * frametime) : 0;
1729 RotRegen(this, RES_HEALTH, limit_mod,
1730 regenstable, regen_health, regen_health_linear, regenframetime,
1731 rotstable, regen_health_rot, regen_health_rotlinear, rotframetime);
1732 }
1733
1734 // if player rotted to death... die!
1735 // check this outside above checks, as player may still be able to rot to death
1736 if(GetResource(this, RES_HEALTH) < 1)
1737 {
1738 if(this.vehicle)
1740 if(this.event_damage)
1741 this.event_damage(this, this, this, 1, DEATH_ROT.m_id, DMG_NOWEP, this.origin, '0 0 0');
1742 }
1743
1744 if (!(this.items & IT_UNLIMITED_AMMO))
1745 {
1748 regenframetime = ((time > this.pauseregen_finished) && (this.items & ITEM_FuelRegen.m_itemid)) ? frametime : 0;
1749 rotframetime = (time > this.pauserotfuel_finished) ? frametime : 0;
1750 RotRegen(this, RES_FUEL, 1,
1753 }
1754}
1755
1757void SetZoomState(entity this, float newzoom)
1758{
1759 if(newzoom != CS(this).zoomstate)
1760 {
1761 CS(this).zoomstate = newzoom;
1762 ClientData_Touch(this, true);
1763 }
1764 zoomstate_set = true;
1765}
1766
1768{
1770 if (game_stopped)
1771 {
1772 CS(this).pressedkeys = 0;
1773 STAT(PRESSED_KEYS, this) = 0;
1774 return;
1775 }
1776
1777 // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
1778 int keys = STAT(PRESSED_KEYS, this);
1779 keys = BITSET(keys, KEY_FORWARD, CS(this).movement.x > 0);
1780 keys = BITSET(keys, KEY_BACKWARD, CS(this).movement.x < 0);
1781 keys = BITSET(keys, KEY_RIGHT, CS(this).movement.y > 0);
1782 keys = BITSET(keys, KEY_LEFT, CS(this).movement.y < 0);
1783
1784 keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this));
1785 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
1786 keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
1787 keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
1788 CS(this).pressedkeys = keys; // store for other users
1789
1790 STAT(PRESSED_KEYS, this) = keys;
1791}
1792
1793/*
1794======================
1795spectate mode routines
1796======================
1797*/
1798
1799void SpectateCopy(entity this, entity spectatee)
1800{
1801 TC(Client, this); TC(Client, spectatee);
1802
1803 MUTATOR_CALLHOOK(SpectateCopy, spectatee, this);
1804 PS(this) = PS(spectatee);
1805 this.armortype = spectatee.armortype;
1807 SetResourceExplicit(this, RES_CELLS, GetResource(spectatee, RES_CELLS));
1808 SetResourceExplicit(this, RES_SHELLS, GetResource(spectatee, RES_SHELLS));
1809 SetResourceExplicit(this, RES_BULLETS, GetResource(spectatee, RES_BULLETS));
1810 SetResourceExplicit(this, RES_ROCKETS, GetResource(spectatee, RES_ROCKETS));
1811 SetResourceExplicit(this, RES_FUEL, GetResource(spectatee, RES_FUEL));
1812 this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
1813 SetResourceExplicit(this, RES_HEALTH, GetResource(spectatee, RES_HEALTH));
1814 CS(this).impulse = 0;
1815 this.disableclientprediction = 1; // no need to run prediction on a spectator
1816 this.items = spectatee.items;
1817 STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee);
1818 STAT(HIT_TIME, this) = STAT(HIT_TIME, spectatee);
1819 STAT(AIR_FINISHED, this) = STAT(AIR_FINISHED, spectatee);
1820 STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
1821 STAT(WEAPONS, this) = STAT(WEAPONS, spectatee);
1822 this.punchangle = spectatee.punchangle;
1823 this.view_ofs = spectatee.view_ofs;
1824 this.velocity = spectatee.velocity;
1825 this.dmg_take = spectatee.dmg_take;
1826 this.dmg_save = spectatee.dmg_save;
1827 this.dmg_inflictor = spectatee.dmg_inflictor;
1828 this.v_angle = spectatee.v_angle;
1829 this.angles = spectatee.v_angle;
1830 this.viewloc = spectatee.viewloc;
1831 if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
1832 this.fixangle = true;
1833 setorigin(this, spectatee.origin);
1834 setsize(this, spectatee.mins, spectatee.maxs);
1835 SetZoomState(this, CS(spectatee).zoomstate);
1836
1837 anticheat_spectatecopy(this, spectatee);
1838 STAT(HUD, this) = STAT(HUD, spectatee);
1839 if(spectatee.vehicle)
1840 {
1841 this.angles = spectatee.v_angle;
1842
1843 //this.fixangle = false;
1844 //this.velocity = spectatee.vehicle.velocity;
1845 this.vehicle_health = spectatee.vehicle_health;
1846 this.vehicle_shield = spectatee.vehicle_shield;
1847 this.vehicle_energy = spectatee.vehicle_energy;
1848 this.vehicle_ammo1 = spectatee.vehicle_ammo1;
1849 this.vehicle_ammo2 = spectatee.vehicle_ammo2;
1850 this.vehicle_reload1 = spectatee.vehicle_reload1;
1851 this.vehicle_reload2 = spectatee.vehicle_reload2;
1852
1853 //msg_entity = this;
1854
1855 // WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
1856 //WriteAngle(MSG_ONE, spectatee.v_angle.x);
1857 // WriteAngle(MSG_ONE, spectatee.v_angle.y);
1858 // WriteAngle(MSG_ONE, spectatee.v_angle.z);
1859
1860 //WriteByte (MSG_ONE, SVC_SETVIEW);
1861 // WriteEntity(MSG_ONE, this);
1862 //makevectors(spectatee.v_angle);
1863 //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/
1864 }
1865}
1866
1868{
1869 if(!this.enemy)
1870 return false;
1871
1872 if(!IS_PLAYER(this.enemy) || this == this.enemy)
1873 {
1874 SetSpectatee(this, NULL);
1875 return false;
1876 }
1877
1878 SpectateCopy(this, this.enemy);
1879 if (IS_OBSERVER(this))
1880 TRANSMUTE(Spectator, this);
1881
1882 return true;
1883}
1884
1886{
1887 if(!IS_PLAYER(this.enemy))
1888 return false;
1889
1890 ClientData_Touch(this.enemy, true);
1891
1892 msg_entity = this;
1894 WriteEntity(MSG_ONE, this.enemy);
1896 accuracy_resend(this);
1897
1898 if(!SpectateUpdate(this))
1899 PutObserverInServer(this, false, true);
1900
1901 return true;
1902}
1903
1904void SetSpectatee_status(entity this, int spectatee_num)
1905{
1906 int oldspectatee_status = CS(this).spectatee_status;
1907 CS(this).spectatee_status = spectatee_num;
1908
1909 if (CS(this).spectatee_status != oldspectatee_status)
1910 {
1911 if (STAT(PRESSED_KEYS, this))
1912 {
1913 CS(this).pressedkeys = 0;
1914 STAT(PRESSED_KEYS, this) = 0;
1915 }
1916
1917 ClientData_Touch(this, true);
1918
1919 // init or clear race data
1920 if ((g_race || g_cts) && g_race_qualifying && IS_REAL_CLIENT(this))
1921 {
1922 msg_entity = this;
1923
1924 if (this.enemy && this.enemy.race_laptime)
1925 {
1926 // init
1928 }
1929 else
1930 {
1931 // send reset to this spectator
1932 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1934 }
1935 }
1936 }
1937}
1938
1939void SetSpectatee(entity this, entity spectatee)
1940{
1941 if(IS_BOT_CLIENT(this))
1942 return; // bots abuse .enemy, this code is useless to them
1943
1944 entity old_spectatee = this.enemy;
1945
1946 this.enemy = spectatee;
1947
1948 // WEAPONTODO
1949 // these are required to fix the spectator bug with arc
1950 if(old_spectatee)
1951 {
1952 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
1953 {
1954 .entity weaponentity = weaponentities[slot];
1955 if(old_spectatee.(weaponentity).arc_beam)
1956 old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_UPDATE;
1957 }
1958 }
1959 if(spectatee)
1960 {
1961 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
1962 {
1963 .entity weaponentity = weaponentities[slot];
1964 if(spectatee.(weaponentity).arc_beam)
1965 spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_UPDATE;
1966 }
1967 }
1968
1969 if (spectatee)
1970 SetSpectatee_status(this, etof(spectatee));
1971
1972 // needed to update spectator list
1973 if(old_spectatee)
1974 ClientData_Touch(old_spectatee, true);
1975}
1976
1977bool Spectate(entity this, entity pl)
1978{
1979 if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
1980 return false;
1981 pl = M_ARGV(1, entity);
1982
1983 SetSpectatee(this, pl);
1984 return SpectateSet(this);
1985}
1986
1988{
1989 entity ent = find(this.enemy, classname, STR_PLAYER);
1990
1991 if (MUTATOR_CALLHOOK(SpectateNext, this, ent))
1992 ent = M_ARGV(1, entity);
1993 else if (!ent)
1994 ent = find(ent, classname, STR_PLAYER);
1995
1996 if(ent) SetSpectatee(this, ent);
1997
1998 return SpectateSet(this);
1999}
2000
2002{
2003 // NOTE: chain order is from the highest to the lower entnum (unlike find)
2004 entity ent = findchain(classname, STR_PLAYER);
2005 if (!ent) // no player
2006 return false;
2007
2008 entity first = ent;
2009 // skip players until current spectated player
2010 if(this.enemy)
2011 while(ent && ent != this.enemy)
2012 ent = ent.chain;
2013
2014 switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first))
2015 {
2016 case MUT_SPECPREV_FOUND:
2017 ent = M_ARGV(1, entity);
2018 break;
2020 return true;
2022 default:
2023 {
2024 if(ent.chain)
2025 ent = ent.chain;
2026 else
2027 ent = first;
2028 break;
2029 }
2030 }
2031
2032 SetSpectatee(this, ent);
2033 return SpectateSet(this);
2034}
2035
2036/*
2037=============
2038ShowRespawnCountdown()
2039
2040Update a respawn countdown display.
2041=============
2042*/
2044{
2045 float number;
2046 if(!IS_DEAD(this)) // just respawned?
2047 return;
2048 else
2049 {
2050 number = ceil(this.respawn_time - time);
2051 if(number <= 0)
2052 return;
2053 if(number <= this.respawn_countdown)
2054 {
2055 this.respawn_countdown = number - 1;
2056 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
2057 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number));
2058 }
2059 }
2060}
2061
2063{
2065 return false;
2066 if (QueuedPlayersReady(this, true))
2067 return false;
2068 if (frametime) // once per frame is more than enough
2069 stuffcmd(this, "_scoreboard_team_selection 1\n");
2070 return true;
2071}
2072
2074void Join(entity this, bool queued_join)
2075{
2076 entity player_with_dibs = NULL;
2077
2079 {
2081 ReadyRestart(true);
2083 }
2084
2085 // If teams aren't equal and a player is queued then they chose a biggest team so they can't join yet.
2086 if (queued_join && TeamBalance_AreEqual(this, true))
2087 {
2088 int clients_remaining = AVAILABLE_TEAMS;
2089 // First we must join player(s) queued for specific team(s) (they chose first)
2090 // so TeamBalance_JoinBestTeam() won't select the same team(s).
2091 // Assumes `this` was already assigned a team via ClientCommand_selectteam() or is using autoselect.
2092 FOREACH_CLIENT(it != this && it.wants_join > 0,
2093 {
2094 // detect any conflict between `this` and a queued player (queuePlayer() handles other conflicts)
2095 if (this.team < 0 && this.team_selected > 0 // `this` can't have their preference
2096 && it.wants_join == this.team_selected) // `it` is the player who already chose the team `this` wanted
2097 player_with_dibs = it;
2098
2099 Join(it, false);
2100 --clients_remaining;
2101 });
2102
2103 // Second pass: queued players whose team will be autoselected
2104 // in order of strongest to weakest for optimal balance.
2105 for (; clients_remaining; --clients_remaining)
2106 {
2107 entity strongest_autoselect_client = NULL;
2108 float strongest_autoselect_client_skill = 0;
2109 FOREACH_CLIENT(it.wants_join < 0 || (it == this && it.team <= 0),
2110 {
2111 float it_skill = it.m_skill_mu ? it.m_skill_mu : server_skill_average; // average was set by TeamBalance_AreEqual()
2112 if (it_skill > strongest_autoselect_client_skill)
2113 {
2114 strongest_autoselect_client = it;
2115 strongest_autoselect_client_skill = it_skill;
2116 }
2117 });
2118 if (strongest_autoselect_client == this) // can't just Join(); return; due to player_with_dibs
2119 {
2120 entity balance = TeamBalance_CheckAllowedTeams(this);
2121 int best_team_index = TeamBalance_FindBestTeam(balance, this, true);
2122 TeamBalance_Destroy(balance);
2123 SetPlayerTeam(this, best_team_index, TEAM_CHANGE_AUTO);
2124 }
2125 else if (strongest_autoselect_client)
2126 Join(strongest_autoselect_client, false);
2127 }
2128 }
2129
2130 Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
2131
2132 TRANSMUTE(Player, this);
2133 PutClientInServer(this);
2134
2135 if(IS_PLAYER(this)) // could be false due to PutClientInServer() mutator hook
2136 {
2137 if (!teamplay)
2138 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
2139 else if (player_with_dibs)
2140 // limitation: notifications support only 1 translated team name
2141 // so the team `this` preferred can't be mentioned, only the team they got assigned to.
2142 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PLAY_TEAM_QUEUECONFLICT), player_with_dibs.netname);
2143 else
2144 {
2145 if (this.wants_join && game_starttime < time && !warmup_stage) // No countdown running && not returning to warmup via ReadyRestart_force
2146 Send_Notification(NOTIF_ONE_ONLY, this, MSG_ANNCE, ANNCE_BEGIN); // Get queued player's attention
2147 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PLAY_TEAM));
2148 }
2149 }
2150
2151 this.team_selected = 0;
2152 this.wants_join = 0;
2153}
2154
2156{
2157 if(g_duel)
2158 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)
2159 // don't return map_maxplayers during intermission as it would interfere with MapHasRightSize()
2161 MUTATOR_CALLHOOK(GetPlayerLimit, player_limit);
2162 player_limit = M_ARGV(0, int);
2163 return player_limit < maxclients && player_limit > 0 ? player_limit : 0;
2164}
2165
2172{
2173 if(!this)
2174 // this is called that way when checking if anyone may be able to join (to build qcstatus)
2175 // so report 0 free slots if restricted
2176 {
2177 if(autocvar_g_forced_team_otherwise == "spectate"
2178 || autocvar_g_forced_team_otherwise == "spectator"
2179 || (teamplay && lockteams))
2180 return 0;
2181 }
2182
2183 int totalClients = 0;
2184 int currentlyPlaying = 0;
2185 FOREACH_CLIENT(it != this,
2186 {
2187 ++totalClients;
2188 if(IS_PLAYER(it) || INGAME(it))
2189 ++currentlyPlaying;
2190 });
2191
2192 int player_limit = GetPlayerLimit();
2193
2194 int free_slots = max(0, (player_limit ? player_limit : maxclients) - currentlyPlaying);
2195 if (this || maxclients - totalClients) // don't add bot slots in the getstatus case if nobody can connect
2196 free_slots += bots_would_leave;
2197 if (!this) // getstatus case
2198 free_slots = min(free_slots, maxclients - totalClients);
2199
2200 if(this && !free_slots)
2201 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT, player_limit);
2202
2203 return free_slots;
2204}
2205
2206// Callsites other than ClientCommand_selectteam() should pass this.wants_join as team_index
2207// so the player won't accidentally reset a specific preference by pressing +jump
2208// and will see the centreprint with their current preference each time they press +jump.
2209bool queuePlayer(entity this, int team_index)
2210{
2211 // check if a queued player already chose the selected team
2212 if (team_index > 0)
2213 {
2214 FOREACH_CLIENT(it != this && it.wants_join == team_index,
2215 {
2216 if (QueuedPlayersReady(this, false))
2217 {
2218 // Join() will handle the notification so it can mention the team `player` will actually get
2219 this.team = -1; // force autoselect in Join() (last player skips queue)
2220 this.team_selected = team_index; // tell it which team to check for to find the conflict
2221 }
2222 else // > 2 teams
2223 {
2224 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(Team_IndexToTeam(team_index), CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT), it.netname);
2225 this.wants_join = -1; // force autoselect in Join()
2226 this.team_selected = -1; // prevents clobbering by CENTER_JOIN_PREVENT_QUEUE
2227 }
2228 return true;
2229 });
2230 }
2231
2232 if (QueuedPlayersReady(this, false))
2233 return false;
2234
2235 if (team_index <= 0) // team auto select deferred until Join()
2236 {
2237 if (team_index != this.wants_join || !this.wants_join) // prevents chatcon spam
2238 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_WANTS, this.netname);
2239 if (this.team_selected >= 0) // prevents CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT getting clobbered
2240 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE);
2241 this.wants_join = -1;
2242 this.team_selected = 0;
2243 }
2244 else
2245 {
2246 int team_num = Team_IndexToTeam(team_index);
2247 if (team_index != this.wants_join) // prevents chatcon spam
2248 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(team_num, INFO_JOIN_WANTS_TEAM), this.netname);
2249 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(team_num, CENTER_JOIN_PREVENT_QUEUE_TEAM));
2250 this.wants_join = team_index; // Player queued to join
2251 this.team_selected = team_index;
2252 }
2253
2254 return true;
2255}
2256
2258bool joinAllowed(entity this, int team_index)
2259{
2260 if (CS(this).version_mismatch)
2261 {
2262 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_VERSIONMISMATCH);
2263 return false;
2264 }
2265 if (time < CS(this).jointime + MIN_SPEC_TIME) return false;
2266 if (teamplay && lockteams)
2267 {
2268 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_LOCKED);
2269 return false;
2270 }
2271
2272 if (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR) return false;
2273
2274 if (!INGAME(this) && PlayerInList(this, autocvar_g_playban_list))
2275 {
2276 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PLAYBAN);
2277 return false;
2278 }
2279
2280 // IS_PLAYER for most gametypes, INGAME for ca, lms, surv
2281 if (autocvar_g_maxping && !(IS_PLAYER(this) || INGAME(this)) && !this.wants_join)
2282 {
2283 if (this.ping <= 0) return false; // too soon
2284 if (this.ping > autocvar_g_maxping)
2285 {
2286 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_PING);
2287 return false;
2288 }
2289 }
2290
2291 if (QueueNeeded(this, team_index))
2292 {
2293 if (team_index == 0) // so ClientCommand_selectteam() can check joinAllowed() before calling SetPlayerTeam() without chicken/egg problem
2294 if (ShowTeamSelection(this)) return false; // only needed by callsites other than selectteam
2295 // queuePlayer called here so that only conditions above block queuing (g_maxplayers shouldn't)
2296 if (queuePlayer(this, team_index)) return false;
2297 if (!nJoinAllowed(this)) return false;
2298 }
2299 else
2300 {
2301 if (!nJoinAllowed(this)) return false;
2302 if (team_index == 0) // so ClientCommand_selectteam() can check joinAllowed() before calling SetPlayerTeam() without chicken/egg problem
2303 if (ShowTeamSelection(this)) return false; // only needed by callsites other than selectteam
2304 }
2305
2306 return true;
2307}
2308
2310{
2311 // waypoint editor implements a similar feature for waypoints
2313 return;
2314
2315 if (wasfreed(this.wp_aimed))
2316 this.wp_aimed = NULL;
2317
2319 entity ent = NULL;
2320 if (trace_ent)
2321 {
2322 ent = trace_ent;
2323 if (ent != this.wp_aimed)
2324 {
2325 string str = sprintf(
2326 "^7ent #%d\n^8 netname: ^3%s\n^8 classname: ^5%s\n^8 origin: ^2'%s'",
2327 etof(ent), ent.netname, ent.classname, vtos(ent.origin));
2328 debug_text_3d((ent.absmin + ent.absmax) * 0.5, str, 0, 7, '0 0 0');
2329 }
2330 }
2331 if (this.wp_aimed != ent)
2332 this.wp_aimed = ent;
2333}
2334
2337{
2339 {
2340 this.modelflags &= ~MF_ROCKET;
2342 IntermissionThink(this);
2343 return false;
2344 }
2345
2347 {
2348 // don't allow the player to turn around while game is paused
2349 // FIXME turn this into CSQC stuff
2350 this.v_angle = this.lastV_angle;
2351 this.angles = this.lastV_angle;
2352 this.fixangle = true;
2353 }
2354
2355 if (frametime) player_powerups(this);
2356
2358
2359 if (IS_DEAD(this))
2360 {
2361 if (this.personal && g_race_qualifying
2362 && (autocvar_g_allow_checkpoints || CheatsAllowed(this, CHIMPULSE_SPEEDRUN.impulse, 0, 0, false, true)))
2363 {
2364 if (time > this.respawn_time)
2365 {
2366 STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
2367 respawn(this);
2368 CS(this).impulse = CHIMPULSE_SPEEDRUN.impulse;
2369 }
2370 }
2371 else
2372 {
2373 if (frametime) player_anim(this);
2374
2375 if (this.respawn_flags & RESPAWN_DENY)
2376 {
2377 STAT(RESPAWN_TIME, this) = 0;
2378 return false;
2379 }
2380
2381 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));
2382
2383 switch(this.deadflag)
2384 {
2385 case DEAD_DYING:
2386 {
2387 if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
2389 else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
2390 this.deadflag = DEAD_DEAD;
2391 break;
2392 }
2393 case DEAD_DEAD:
2394 {
2395 if (button_pressed)
2397 else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
2399 break;
2400 }
2401 case DEAD_RESPAWNABLE:
2402 {
2403 if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
2405 break;
2406 }
2407 case DEAD_RESPAWNING:
2408 {
2409 if (time > this.respawn_time)
2410 {
2411 this.respawn_time = time + 1; // only retry once a second
2412 this.respawn_time_max = this.respawn_time;
2413 respawn(this);
2414 }
2415 break;
2416 }
2417 }
2418
2420
2421 if (this.respawn_flags & RESPAWN_SILENT)
2422 STAT(RESPAWN_TIME, this) = 0;
2423 else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
2424 {
2425 if (time < this.respawn_time)
2426 STAT(RESPAWN_TIME, this) = this.respawn_time;
2427 else if (this.deadflag != DEAD_RESPAWNING)
2428 STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
2429 }
2430 else
2431 STAT(RESPAWN_TIME, this) = this.respawn_time;
2432 }
2433
2434 // if respawning, invert stat_respawn_time to indicate this, the client translates it
2435 if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
2436 STAT(RESPAWN_TIME, this) *= -1;
2437
2438 return false;
2439 }
2440
2441 FixPlayermodel(this);
2442
2444 {
2446 stuffcmd(this, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin));
2447 }
2448
2449 // reset gun alignment when dual wielding status changes
2450 // to ensure guns are always aligned right and left
2451 bool dualwielding = W_DualWielding(this);
2452 if(this.dualwielding_prev != dualwielding)
2453 {
2454 W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
2455 this.dualwielding_prev = dualwielding;
2456 }
2457
2458 // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
2459 //if(frametime)
2460 {
2461 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2462 {
2463 .entity weaponentity = weaponentities[slot];
2464 if(WEP_CVAR(WEP_VORTEX, charge_always))
2465 W_Vortex_Charge(this, weaponentity, frametime);
2466 W_WeaponFrame(this, weaponentity);
2467 }
2468 }
2469
2470 if (frametime)
2471 {
2472 // WEAPONTODO: Add a weapon request for this
2473 // rot vortex charge to the charge limit
2474 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2475 {
2476 .entity weaponentity = weaponentities[slot];
2477 if (WEP_CVAR(WEP_VORTEX, charge_rot_rate) && this.(weaponentity).vortex_charge > WEP_CVAR(WEP_VORTEX, charge_limit) && this.(weaponentity).vortex_charge_rottime < time)
2478 this.(weaponentity).vortex_charge = bound(WEP_CVAR(WEP_VORTEX, charge_limit), this.(weaponentity).vortex_charge - WEP_CVAR(WEP_VORTEX, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
2479 }
2480
2481 player_regen(this);
2482 player_anim(this);
2484 }
2485
2486 monsters_setstatus(this);
2487
2488 return true;
2489}
2490
2492// merged SpectatorThink and ObserverThink (old names are here so you can grep for them)
2494{
2495 bool is_spec = IS_SPEC(this);
2496 if ( CS(this).impulse )
2497 {
2498 int r = MinigameImpulse(this, CS(this).impulse);
2499 if (!is_spec || r)
2500 CS(this).impulse = 0;
2501
2502 if (is_spec && CS(this).impulse == IMP_weapon_drop.impulse)
2503 {
2504 STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3;
2505 CS(this).impulse = 0;
2506 return;
2507 }
2508 }
2509
2511
2512 if (IS_BOT_CLIENT(this) && !CS(this).autojoin_checked)
2513 {
2514 CS(this).autojoin_checked = 1;
2515 TRANSMUTE(Player, this);
2516 PutClientInServer(this);
2517
2518 .entity weaponentity = weaponentities[0];
2519 if(this.(weaponentity).m_weapon == WEP_Null)
2520 W_NextWeapon(this, 0, weaponentity);
2521
2522 return;
2523 }
2524
2525 if (this.flags & FL_JUMPRELEASED)
2526 {
2527 if (PHYS_INPUT_BUTTON_JUMP(this) && (joinAllowed(this, this.wants_join) || time < CS(this).jointime + MIN_SPEC_TIME))
2528 {
2529 this.flags &= ~FL_JUMPRELEASED;
2530 this.flags |= FL_SPAWNING;
2531 }
2532 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)))
2533 || (!is_spec && ((PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch) || this.would_spectate)))
2534 {
2535 this.flags &= ~FL_JUMPRELEASED;
2536 if (autocvar_sv_spectate == 2 && !warmup_stage && !this.vote_master)
2537 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_SPECTATE_SPEC_NOTALLOWED);
2538 else if(SpectateNext(this))
2539 { }
2540 else if (is_spec)
2541 {
2542 TRANSMUTE(Observer, this);
2543 PutClientInServer(this);
2544 }
2545 else
2546 this.would_spectate = false; // unable to spectate anyone
2547
2548 if (is_spec)
2549 CS(this).impulse = 0;
2550 }
2551 else if (is_spec)
2552 {
2553 if(CS(this).impulse == 12 || CS(this).impulse == 16 || CS(this).impulse == 19 || (CS(this).impulse >= 220 && CS(this).impulse <= 229))
2554 {
2555 this.flags &= ~FL_JUMPRELEASED;
2556 if (!SpectatePrev(this))
2557 {
2558 TRANSMUTE(Observer, this);
2559 PutClientInServer(this);
2560 }
2561 CS(this).impulse = 0;
2562 }
2563 else if(PHYS_INPUT_BUTTON_ATCK2(this))
2564 {
2566 {
2567 this.would_spectate = false;
2568 this.flags &= ~FL_JUMPRELEASED;
2569 TRANSMUTE(Observer, this);
2570 PutClientInServer(this);
2571 }
2572 }
2573 else if(!SpectateUpdate(this) && !SpectateNext(this))
2574 {
2575 PutObserverInServer(this, false, true);
2576 this.would_spectate = true;
2577 }
2578 }
2579 else
2580 {
2581 bool wouldclip = CS_CVAR(this).cvar_cl_clippedspectating;
2582 if (PHYS_INPUT_BUTTON_USE(this))
2583 wouldclip = !wouldclip;
2584 int preferred_movetype = (wouldclip ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
2585 set_movetype(this, preferred_movetype);
2586 }
2587 }
2588 else
2589 { // jump pressed
2590 if ((is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)))
2591 || (!is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))))
2592 {
2593 this.flags |= FL_JUMPRELEASED;
2594 // primary attack pressed
2595 if(this.flags & FL_SPAWNING)
2596 {
2597 this.flags &= ~FL_SPAWNING;
2598 if(joinAllowed(this, this.wants_join))
2599 Join(this, teamplay);
2600 else if(time < CS(this).jointime + MIN_SPEC_TIME)
2601 CS(this).autojoin_checked = -1;
2602 return;
2603 }
2604 }
2605 if(is_spec && !SpectateUpdate(this))
2606 PutObserverInServer(this, false, true);
2607 }
2608 if (is_spec)
2609 this.flags |= FL_CLIENT | FL_NOTARGET;
2610}
2611
2613{
2614 if (!IS_PLAYER(this))
2615 return;
2616
2617 if(this.vehicle)
2618 {
2619 if(!game_stopped)
2620 {
2622 return;
2623 }
2624 }
2625 else if(autocvar_g_vehicles_enter)
2626 {
2627 if(!game_stopped && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
2628 {
2629 entity head, closest_target = NULL;
2631
2632 while(head) // find the closest acceptable target to enter
2633 {
2634 if(IS_VEHICLE(head) && !IS_DEAD(head) && head.takedamage != DAMAGE_NO)
2635 if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this)))
2636 {
2637 if(closest_target)
2638 {
2639 if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin))
2640 closest_target = head;
2641 }
2642 else
2643 closest_target = head;
2644 }
2645
2646 head = head.chain;
2647 }
2648
2649 if(closest_target)
2650 {
2651 vehicles_enter(this, closest_target);
2652 return;
2653 }
2654 }
2655 }
2656
2657 // a use key was pressed; call handlers
2659}
2660
2661
2662/*
2663=============
2664PlayerPreThink
2665
2666Called every frame for each real client by DP (and for each bot by StartFrame()),
2667and when executing every asynchronous move, so only include things that MUST be done then.
2668Use PlayerFrame() instead for code that only needs to run once per server frame.
2669frametime == 0 in the asynchronous code path.
2670
2671TODO: move more stuff from here and PlayerThink() and ObserverOrSpectatorThink() to PlayerFrame() (frametime is always set there)
2672=============
2673*/
2676{
2678
2679 zoomstate_set = false;
2680
2682
2683 if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed)
2684 PlayerUseKey(this);
2685 CS(this).usekeypressed = PHYS_INPUT_BUTTON_USE(this);
2686
2687 if (IS_PLAYER(this))
2688 {
2689 if (IS_REAL_CLIENT(this) && time < CS(this).jointime + MIN_SPEC_TIME)
2690 error("Client can't be spawned as player on connection!");
2691 if(!PlayerThink(this))
2692 return;
2693 }
2695 {
2697 IntermissionThink(this);
2698 return;
2699 }
2700 else if (IS_REAL_CLIENT(this) && CS(this).autojoin_checked <= 0 && time >= CS(this).jointime + MIN_SPEC_TIME
2701 && this.ping > 0) // delay until centreprints could work (still on loading screen but timers start when it disappears)
2702 {
2703 bool early_join_requested = (CS(this).autojoin_checked < 0);
2704 CS(this).autojoin_checked = 1;
2705 // don't do this in ClientConnect
2706 // many things can go wrong if a client is spawned as player on connection
2707 if (early_join_requested || MUTATOR_CALLHOOK(AutoJoinOnConnection, this)
2710 {
2711 if(joinAllowed(this, this.wants_join))
2712 Join(this, teamplay);
2713 else if (!autocvar_sv_spectate) // we really want to join and some blockers may be brief (g_maxping)
2714 if (time < CS(this).spectatortime + MIN_SPEC_TIME) // centreprints don't appear while spamming
2715 CS(this).autojoin_checked = -1; // keep trying for MIN_SPEC_TIME
2716 return;
2717 }
2718 }
2719 else if (IS_OBSERVER(this) || IS_SPEC(this))
2721
2722 // WEAPONTODO: Add weapon request for this
2723 if (!zoomstate_set)
2724 {
2725 bool wep_zoomed = false;
2726 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2727 {
2728 .entity weaponentity = weaponentities[slot];
2729 Weapon thiswep = this.(weaponentity).m_weapon;
2730 if(thiswep != WEP_Null && thiswep.wr_zoom)
2731 wep_zoomed += thiswep.wr_zoom(thiswep, this);
2732 }
2733 SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed);
2734 }
2735
2736 // Voice sound effects
2737 if (CS(this).teamkill_soundtime && time > CS(this).teamkill_soundtime)
2738 {
2739 CS(this).teamkill_soundtime = 0;
2740
2741 entity e = CS(this).teamkill_soundsource;
2742 entity oldpusher = e.pusher;
2743 e.pusher = this;
2744 PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY, 1);
2745 e.pusher = oldpusher;
2746 }
2747
2748 if (CS(this).taunt_soundtime && time > CS(this).taunt_soundtime)
2749 {
2750 CS(this).taunt_soundtime = 0;
2751 PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT, 1);
2752 }
2753
2755}
2756
2758{
2759 // TODO: mutator hook to prevent drowning?
2760 if(IS_DEAD(this) || game_stopped || time < game_starttime || this.vehicle
2761 || STAT(FROZEN, this) || this.watertype != CONTENT_WATER)
2762 {
2763 STAT(AIR_FINISHED, this) = 0;
2764 return;
2765 }
2766
2767 if (this.waterlevel != WATERLEVEL_SUBMERGED)
2768 {
2769 if(STAT(AIR_FINISHED, this) && STAT(AIR_FINISHED, this) < time)
2770 PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND, 1);
2771 STAT(AIR_FINISHED, this) = 0;
2772 }
2773 else
2774 {
2775 if (!STAT(AIR_FINISHED, this))
2776 STAT(AIR_FINISHED, this) = time + autocvar_g_balance_contents_drowndelay;
2777 if (STAT(AIR_FINISHED, this) < time)
2778 { // drown!
2779 if (this.pain_finished < time)
2780 {
2782 this.pain_finished = time + 0.5;
2783 }
2784 }
2785 }
2786}
2787
2789
2791{
2793
2794 if(!this.move_qcphysics)
2795 return;
2796
2797 if(!frametime && !CS(this).pm_frametime)
2798 return;
2799
2800 Movetype_Physics_NoMatchTicrate(this, CS(this).pm_frametime, true);
2801}
2802
2803/*
2804=============
2805PlayerPostThink
2806
2807Called every frame for each real client by DP (and for each bot by StartFrame()),
2808and when executing every asynchronous move, so only include things that MUST be done then.
2809Use PlayerFrame() instead for code that only needs to run once per server frame.
2810frametime == 0 in the asynchronous code path.
2811=============
2812*/
2814{
2815 Player_Physics(this);
2816
2817 if (IS_PLAYER(this))
2818 {
2819 DrownPlayer(this);
2820 UpdateChatBubble(this);
2821 if (CS(this).impulse)
2822 ImpulseCommands(this);
2823 GetPressedKeys(this);
2824 }
2825 else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
2826 {
2827 CS(this).pressedkeys = 0;
2828 STAT(PRESSED_KEYS, this) = 0;
2829 }
2830
2832 CS(this).pm_frametime = 0; // CSQCModel_CheckUpdate() and Player_Physics() both read it
2833}
2834
2835/*
2836=============
2837PlayerFrame
2838
2839Called every frame for each client by StartFrame().
2840Use this for code that only needs to run once per server frame.
2841frametime is always set here.
2842=============
2843*/
2845{
2846 Physics_UpdateStats(this);
2847
2848 // Don't accumulate alivetime whilst afk as xonstat skill ratings are based on score per second.
2849 // Time accumulated so far (including the 30s) is retained in the event of a move to spec, disconnect, etc.
2850 if (this.alivetime_start && time - CS(this).parm_idlesince >= 30)
2851 this.alivetime_start += frametime;
2852
2853// formerly PreThink code
2854
2855 if (this.score_frame_dmg)
2856 {
2858 GameRules_scoring_add(this, DMG, this.score_frame_dmg);
2859 this.score_frame_dmg = 0;
2860 }
2861 if (this.score_frame_dmgtaken)
2862 {
2864 GameRules_scoring_add(this, DMGTAKEN, this.score_frame_dmgtaken);
2865 this.score_frame_dmgtaken = 0;
2866 }
2867
2868 STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO
2869 STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump;
2870
2871 // physics frames: update anticheat stuff
2872 anticheat_prethink(this);
2873
2874 // Check if spectating is allowed
2875 // cvar hook/callback TODO: make this event-driven
2877 && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this))
2878 {
2879 float cutoff = CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime;
2880 if (time > cutoff + MIN_SPEC_TIME * 0.5 // sv_spectate was disabled recently (or the server was stalled far too long)
2881 || CS(this).autojoin_checked == 0) // or too soon to have tried to autojoin
2882 {
2883 CS(this).spectatortime = time; // reset the grace period
2884 if (CS(this).autojoin_checked) // only notify when sv_spectate was disabled recently, to prevent spam
2885 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
2886 }
2887 else if (time > cutoff)
2888 if (dropclient_schedule(this))
2889 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
2890 }
2891
2892 // Check for nameless players
2893 if (this.netname == "" || this.netname != CS(this).netname_previous)
2894 {
2895 bool assume_unchanged = (CS(this).netname_previous == "");
2896 if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength)
2897 {
2898 int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol);
2899 this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7"));
2900 sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength));
2901 assume_unchanged = false;
2902 // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
2903 }
2904 if (isInvisibleString(this.netname))
2905 {
2906 this.netname = strzone(sprintf("Player#%d", this.playerid));
2907 sprint(this, "Warning: invisible names are not allowed.\n");
2908 assume_unchanged = false;
2909 // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
2910 }
2911 if (!assume_unchanged && autocvar_sv_eventlog)
2912 GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false)));
2913 strcpy(CS(this).netname_previous, this.netname);
2914 }
2915
2916 // version nagging
2917 if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime)
2918 {
2919 CS(this).version_nagtime = 0;
2920 if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0)
2921 {
2922 // git client
2923 }
2924 else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0)
2925 {
2926 // git server
2927 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2928 }
2929 else
2930 {
2931 int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
2932 if (r < 0) // old client
2933 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2934 else if (r > 0) // old server
2935 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2936 }
2937 }
2938
2939 if (this.ignore_list_send_time > 0 && time > this.ignore_list_send_time)
2940 ignore_list_send(this);
2941
2942 // GOD MODE info
2943 if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
2944 {
2945 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
2946 this.max_armorvalue = 0;
2947 }
2948
2949 // Vehicles
2950 if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle)
2951 if(IS_PLAYER(this) && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
2952 {
2954 {
2955 if(!it.owner)
2956 {
2957 if(!it.team || SAME_TEAM(this, it))
2958 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
2959 else if(autocvar_g_vehicles_steal)
2960 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
2961 }
2962 else if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this))
2963 {
2964 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
2965 }
2966 });
2967
2968 this.last_vehiclecheck = time + 1;
2969 }
2970
2971
2972
2973// formerly PostThink code
2975 if (IS_REAL_CLIENT(this))
2977 if (!intermission_running) // NextLevel() kills all centerprints after setting this true
2978 {
2979 int totalClients = 0;
2981 {
2982 // maxidle disabled in local matches by not counting clients (totalClients 0)
2984 {
2986 {
2987 ++totalClients;
2988 });
2989 if (maxclients - totalClients > autocvar_sv_maxidle_slots)
2990 totalClients = 0;
2991 }
2992 }
2993 else if ((IS_PLAYER(this) || this.wants_join) && autocvar_sv_maxidle_playertospectator > 0)
2994 {
2996 {
2997 ++totalClients;
2998 });
2999 }
3000
3001 if (totalClients < autocvar_sv_maxidle_minplayers)
3002 {
3003 // idle kick disabled
3004 CS(this).parm_idlesince = time;
3005 }
3006 else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
3007 {
3008 if (CS(this).idlekick_lasttimeleft)
3009 {
3010 CS(this).idlekick_lasttimeleft = 0;
3011 Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
3012 }
3013 }
3014 else
3015 {
3016 float maxidle_time = autocvar_sv_maxidle;
3017 if ((IS_PLAYER(this) || this.wants_join)
3020 float timeleft = ceil(maxidle_time - (time - CS(this).parm_idlesince));
3021 float countdown_time = max(min(10, maxidle_time - 1), ceil(maxidle_time * 0.33)); // - 1 to support maxidle_time <= 10
3022 if (timeleft == countdown_time && !CS(this).idlekick_lasttimeleft)
3023 {
3025 {
3026 if (!this.wants_join) // no countdown centreprint when getting kicked off the join queue
3027 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOVETOSPEC_IDLING, timeleft);
3028 }
3029 else
3030 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
3031 }
3032 if (timeleft <= 0)
3033 {
3034 if ((IS_PLAYER(this) || this.wants_join)
3036 {
3037 if (this.wants_join)
3038 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING_QUEUE, this.netname, maxidle_time);
3039 else
3040 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING, this.netname, maxidle_time);
3041 PutObserverInServer(this, true, true);
3042 // Can't do this in PutObserverInServer() or SetPlayerTeam() cos it causes
3043 // mouse2 (change spectate mode) to kick the player off the join queue.
3044 this.wants_join = 0;
3045 this.team_selected = 0;
3046 // when the player is kicked off the server, these are called in ClientDisconnect()
3050 }
3051 else
3052 {
3053 if (dropclient_schedule(this))
3054 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname, maxidle_time);
3055 }
3056 return;
3057 }
3058 else if (timeleft <= countdown_time
3059 && !this.wants_join) // no countdown bangs when getting kicked off the join queue
3060 {
3061 if (timeleft != CS(this).idlekick_lasttimeleft)
3062 play2(this, SND(TALK2));
3063 CS(this).idlekick_lasttimeleft = timeleft;
3064 }
3065 }
3066 }
3067
3068 CheatFrame(this);
3069
3070 if (game_stopped)
3071 {
3072 this.solid = SOLID_NOT;
3073 this.takedamage = DAMAGE_NO;
3075 CS(this).teamkill_complain = 0;
3076 CS(this).teamkill_soundtime = 0;
3077 CS(this).teamkill_soundsource = NULL;
3078 }
3079
3081 float hp = healtharmor_maxdamage(GetResource(this, RES_HEALTH), GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x;
3083 }
3084}
3085
3086// hack to copy the button fields from the client entity to the Client State
3088{
3089 if(this.impulse)
3090 store.impulse = this.impulse;
3091 this.impulse = 0;
3092
3093 bool typing = this.buttonchat || this.button12;
3094
3095 store.button0 = (typing) ? 0 : this.button0;
3096 //button1?!
3097 store.button2 = (typing) ? 0 : this.button2;
3098 store.button3 = (typing) ? 0 : this.button3;
3099 store.button4 = this.button4;
3100 store.button5 = (typing) ? 0 : this.button5;
3101 store.button6 = this.button6;
3102 store.button7 = this.button7;
3103 store.button8 = this.button8;
3104 store.button9 = this.button9;
3105 store.button10 = this.button10;
3106 store.button11 = this.button11;
3107 store.button12 = this.button12;
3108 store.button13 = this.button13;
3109 store.button14 = this.button14;
3110 store.button15 = this.button15;
3111 store.button16 = this.button16;
3112 store.buttonuse = this.buttonuse;
3113 store.buttonchat = this.buttonchat;
3114
3115 store.cursor_active = this.cursor_active;
3116 store.cursor_screen = this.cursor_screen;
3117 store.cursor_trace_start = this.cursor_trace_start;
3118 store.cursor_trace_endpos = this.cursor_trace_endpos;
3119 store.cursor_trace_ent = this.cursor_trace_ent;
3120
3121 store.ping = this.ping;
3122 store.ping_packetloss = this.ping_packetloss;
3123 store.ping_movementloss = this.ping_movementloss;
3124
3125 store.v_angle = this.v_angle;
3126 store.movement = this.movement;
3127}
3128
3129NET_HANDLE(fpsreport, bool)
3130{
3131 int fps = ReadShort();
3132 PlayerScore_Set(sender, SP_FPS, fps);
3133 return true;
3134}
void accuracy_resend(entity e)
force a resend of a player's accuracy stats
Definition accuracy.qc:80
float frame
primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
Definition anim.qh:6
void anticheat_spectatecopy(entity this, entity spectatee)
Definition anticheat.qc:166
void anticheat_prethink(entity this)
Definition anticheat.qc:172
void antilag_clear(entity e, entity store)
Definition antilag.qc:114
int autocvar_g_antilag
Definition antilag.qh:3
float bot_forced_team
Definition api.qh:41
void navigation_dynamicgoal_init(entity this, bool initially_static)
Definition navigation.qc:77
float bot_attack
Definition api.qh:38
IntrusiveList g_bot_targets
Definition api.qh:149
void bot_aim_reset(entity this)
Definition aim.qc:134
int bots_would_leave
how many bots would leave so humans can replace them
Definition api.qh:101
int player_count
Definition api.qh:103
void bot_relinkplayerlist()
Definition bot.qc:424
const int ARC_SF_UPDATE
Definition arc.qh:124
vector arc_shotorigin[4]
Definition arc.qh:108
entity arc_beam
Definition arc.qh:132
#define g_assault
Definition assault.qh:27
string autocvar_g_playban_list
Definition banning.qh:14
string autocvar_g_chatban_list
Definition banning.qh:13
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define MUTATOR_IS_ENABLED(this)
Definition base.qh:193
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
#define BITSET(var, mask, flag)
Definition bits.qh:11
#define boolean(value)
Definition bool.qh:9
float CheatFrame(entity this)
Definition cheats.qc:707
bool drag_undraggable(entity draggee, entity dragger)
Definition cheats.qc:900
float CheatsAllowed(entity this, float imp, int argc, float cheatframe, bool logattempt, bool ignoredead)
Definition cheats.qc:59
bool speedrunning
Definition cheats.qh:22
entity personal
Definition cheats.qh:24
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
vector punchangle
float pain_finished
entity active_minigame
void SetResource(entity e, Resource res_type, float amount)
Sets the current amount of resource the given entity will have.
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
const int MAX_AXH
entity AuxiliaryXhair[MAX_AXH]
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
virtual void wr_zoom()
(BOTH) weapon specific zoom reticle
Definition weapon.qh:147
string netname
Definition powerups.qc:20
float cnt
Definition powerups.qc:24
vector colormod
Definition powerups.qc:21
float count
Definition powerups.qc:22
float alpha
Definition items.qc:13
float ping
Definition main.qh:169
float ping_movementloss
Definition main.qh:169
float ping_packetloss
Definition main.qh:169
entity owner
Definition main.qh:87
bool ready
Definition main.qh:88
int spectatee_status
the -1 disables HUD panels before CSQC receives necessary data
Definition main.qh:197
string ignore_list
Definition main.qh:130
bool warmup_stage
Definition main.qh:120
int serverflags
Definition main.qh:211
vector hook_shotorigin[4]
Definition main.qh:205
int team
Definition main.qh:188
entity killindicator
Definition clientkill.qh:7
void ignore_list_update_on_connection(entity this)
Updates ignore list of all the players.
Definition cmd.qc:153
void ignore_list_send(entity this)
Definition cmd.qc:168
void ignore_remove_player(entity this, entity ignore, bool from_db_too)
Removes a player from the ignore list of another player.
Definition cmd.qc:47
float ignore_list_send_time
Definition cmd.qh:9
const int IGNORE_LIST_SEND_NOW
Definition cmd.qh:8
#define debug_text_3d(...)
Definition debug.qh:486
#define g_race
Definition race.qh:48
const int IT_USING_JETPACK
Definition item.qh:27
const int IT_SUPERWEAPON
Definition item.qh:41
string mdl
Definition item.qh:89
const int IT_UNLIMITED_AMMO
Definition item.qh:23
const int IT_UNLIMITED_SUPERWEAPONS
Definition item.qh:24
#define setmodel(this, m)
Definition model.qh:26
#define M_ARGV(x, type)
Definition events.qh:17
void Physics_UpdateStats(entity this)
Definition player.qc:44
int items
Definition player.qh:226
vector autocvar_sv_player_viewoffset
Definition player.qh:337
vector movement
Definition player.qh:228
#define IS_CLIENT(s)
Definition player.qh:241
#define IS_DEAD(s)
Definition player.qh:244
#define UNSET_DUCKED(s)
Definition player.qh:214
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition player.qh:153
float autocvar_sv_jumpspeedcap_min
Definition player.qh:49
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition player.qh:157
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:161
vector v_angle
Definition player.qh:236
float watertype
Definition player.qh:224
float waterlevel
Definition player.qh:225
#define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s)
Definition player.qh:163
#define IS_DUCKED(s)
Definition player.qh:212
#define PHYS_INPUT_BUTTON_MINIGAME(s)
Definition player.qh:166
#define IS_PLAYER(s)
Definition player.qh:242
float autocvar_sv_jumpspeedcap_max
Definition player.qh:47
#define PHYS_INPUT_BUTTON_USE(s)
Definition player.qh:160
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:152
#define PHYS_INPUT_BUTTON_ZOOM(s)
Definition player.qh:155
entity conveyor
Definition player.qh:57
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:154
int timeout_status
Definition stats.qh:87
float game_starttime
Definition stats.qh:82
float game_stopped
Definition stats.qh:81
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2178
vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
Definition util.qc:1389
Notification Announcer_PickNumber(int type, int num)
Definition util.qc:1924
float compressShotOrigin(vector v)
Definition util.qc:1348
float textLengthUpToLength(string theText, int maxLength, textLengthUpToLength_lenFunction_t w)
Definition util.qc:918
float get_model_parameters(string m, float sk)
Definition util.qc:1492
string mi_shortname
Definition util.qh:126
float get_model_parameters_species
Definition util.qh:180
string strtolower(string s)
const int CNT_RESPAWN
Definition util.qh:255
const int FL_CLIENT
Definition constants.qh:72
const int KEY_JUMP
Definition constants.qh:39
const int KEY_ATCK2
Definition constants.qh:42
const int KEY_RIGHT
Definition constants.qh:38
const int FL_PICKUPITEMS
Definition constants.qh:88
const int FL_NOTARGET
Definition constants.qh:76
const int FL_JUMPRELEASED
Definition constants.qh:81
const int KEY_BACKWARD
Definition constants.qh:36
const int FL_SPAWNING
Definition constants.qh:87
const int HUD_NORMAL
Definition constants.qh:47
const int KEY_FORWARD
Definition constants.qh:35
const int FL_GODMODE
Definition constants.qh:75
const int KEY_LEFT
Definition constants.qh:37
const int KEY_CROUCH
Definition constants.qh:40
const int FRAGS_PLAYER
Definition constants.qh:3
const int KEY_ATCK
Definition constants.qh:41
const int SPECIES_HUMAN
Definition constants.qh:22
const int FRAGS_SPECTATOR
Definition constants.qh:4
#define EFMASK_CHEAP
Definition constants.qh:107
IntrusiveList g_conveyed
Definition conveyor.qh:4
float counter_cnt
Definition counter.qh:6
IntrusiveList g_counters
Definition counter.qh:8
string classname
float movetype
float flags
const float SOLID_SLIDEBOX
float maxclients
entity trace_ent
float DPCONTENTS_BOTCLIP
float DPCONTENTS_SOLID
vector avelocity
float frametime
vector velocity
float DPCONTENTS_BODY
const float CONTENT_WATER
const float EF_FULLBRIGHT
const float SOLID_NOT
float effects
float DPCONTENTS_PLAYERCLIP
float time
float nextthink
const float EF_NODEPTHTEST
vector origin
vector oldorigin
float solid
const float CONTENT_EMPTY
const float EF_NODRAW
float dphitcontentsmask
int modelflags
float death_time
const int EF_TELEPORT_BIT
const int MF_ROCKET
const int EF_RESTARTANIM_BIT
#define CSQCMODEL_AUTOUPDATE(e)
#define CSQCMODEL_EF_RESPAWNGHOST
#define CSQCMODEL_AUTOINIT(e)
#define g_cts
Definition cts.qh:36
int autocvar_bot_vs_human
Definition cvars.qh:67
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:483
float taunt_soundtime
Definition damage.qh:59
float dmg_team
Definition damage.qh:53
float damagedbycontents
Definition damage.qh:45
float autocvar_g_balance_armor_blockpercent
Definition damage.qh:21
IntrusiveList g_damagedbycontents
Definition damage.qh:143
float autocvar_g_teamdamage_resetspeed
Definition damage.qh:22
float teamkill_soundtime
Definition damage.qh:55
#define DMG_NOWEP
Definition damage.qh:104
float autocvar_g_balance_damagepush_speedfactor
Definition damage.qh:18
float damageforcescale
string clientstatus
float disableclientprediction
float button10
float button12
float button3
float button7
float button11
float button16
entity drawonlytoclient
float button4
vector cursor_screen
float button6
string netaddress
string crypto_idfp
entity cursor_trace_ent
float EF_LOWPRECISION
float button8
float buttonchat
float buttonuse
float crypto_idfp_signed
float button9
float button15
float button13
vector punchvector
float button14
vector cursor_trace_start
float clientcolors
float button5
vector cursor_trace_endpos
#define strstrofs
#define strlen
#define tokenize_console
#define g_duel
Definition duel.qh:32
bool move_qcphysics
Definition physics.qh:25
void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt)
Definition all.qc:120
RES_ARMOR
Definition ent_cs.qc:155
WriteString(chan, ent.netname)
ent angles
Definition ent_cs.qc:146
model
Definition ent_cs.qc:164
WriteByte(chan, ent.angles.y/DEC_FACTOR)
SetResourceExplicit(ent, RES_ARMOR, ReadByte() *DEC_FACTOR)) ENTCS_PROP(NAME
skin
Definition ent_cs.qc:168
void entcs_update_players(entity player)
Definition ent_cs.qc:208
int frags
Definition ent_cs.qh:67
int wants_join
Definition ent_cs.qh:69
ERASEABLE bool fexists(string f)
Definition file.qh:4
string GameLog_ProcessIP(string s)
Definition gamelog.qc:8
void GameLogEcho(string s)
Definition gamelog.qc:15
bool autocvar_sv_eventlog
Definition gamelog.qh:3
void UpdatePlayerSounds(entity this)
const int VOICETYPE_LASTATTACKER_ONLY
bool autocvar_g_debug_globalsounds
Use new sound handling.
Definition globalsound.qh:9
const int VOICETYPE_PLAYERSOUND
#define PlayerSound(this, def, chan, vol, voicetype, pitchscale)
const int VOICETYPE_AUTOTAUNT
Weapons
Definition guide.qh:113
void Handicap_Initialize(entity player)
Initializes handicap to its default value.
Definition handicap.qc:16
float Handicap_GetTotalHandicap(entity player, bool receiving)
Returns the total handicap of the player.
Definition handicap.qc:96
Header file that describes the handicap system.
float handicap_avg_taken_sum
Definition handicap.qh:72
float handicap_avg_given_sum
Definition handicap.qh:71
void IntermissionThink(entity this)
void Send_NextMap_To_Player(entity pl)
bool intermission_running
#define get_nextmap()
ERASEABLE void IL_REMOVE(IntrusiveList this, entity it)
Remove any element, anywhere in the list.
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_EACH(this, cond, body)
bool Ban_MaybeEnforceBanOnce(entity client)
Definition ipban.qc:471
#define FOREACH_WORD(words, cond, body)
Definition iter.qh:33
#define FOREACH_ENTITY_RADIUS(org, dist, cond, body)
Definition iter.qh:160
#define FOREACH(list, cond, body)
Definition iter.qh:19
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:344
float pushltime
Definition jumppads.qh:21
bool istypefrag
Definition jumppads.qh:22
entity ladder_entity
Definition ladder.qh:11
IntrusiveList g_ladderents
Definition ladder.qh:3
#define PlayerPostThink
Definition _all.inc:262
#define ClientConnect
Definition _all.inc:242
#define PlayerPreThink
Definition _all.inc:258
#define SetChangeParms
Definition _all.inc:232
#define PutClientInServer
Definition _all.inc:250
#define ClientDisconnect
Definition _all.inc:246
#define TC(T, sym)
Definition _all.inc:82
#define SetNewParms
Definition _all.inc:228
int SendFlags
Definition net.qh:159
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:156
#define WriteHeader(to, id)
Definition net.qh:265
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define STAT(...)
Definition stats.qh:94
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition common.qc:660
#define LOG_FATAL(...)
Definition log.qh:50
#define assert(expr,...)
Definition log.qh:8
int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametypeToSet)
Definition mapinfo.qc:1382
void MapInfo_ClearTemps()
Definition mapinfo.qc:1630
int map_minplayers
Definition mapinfo.qh:190
int map_maxplayers
Definition mapinfo.qh:191
string MapInfo_Map_author
Definition mapinfo.qh:10
string MapInfo_Map_titlestring
Definition mapinfo.qh:8
entity viewloc
Definition viewloc.qh:13
bool autocvar_g_campaign
Definition menu.qc:752
void localcmd(string command,...)
float ceil(float f)
float MSG_ONE
Definition menudefs.qc:56
float stof(string val,...)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float cvar(string name)
void sprint(float clientnum, string text,...)
entity find(entity start,.string field, string match)
float random(void)
void WriteEntity(entity data, float dest, float desto)
vector randomvec(void)
void WriteCoord(float data, float dest, float desto)
string vtos(vector v)
float min(float f,...)
string ftos(float f)
float floor(float f)
const string cvar_defstring(string name)
string strzone(string s)
string argv(float n)
float max(float f,...)
#define etof(e)
Definition misc.qh:25
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)
Definition movetypes.qc:779
const int MOVETYPE_WALK
Definition movetypes.qh:136
const int MOVETYPE_NONE
Definition movetypes.qh:133
const int MOVETYPE_QCPLAYER
Definition movetypes.qh:154
const int WATERLEVEL_SUBMERGED
Definition movetypes.qh:14
const int WATERLEVEL_NONE
Definition movetypes.qh:11
const int MOVETYPE_FLY_WORLDONLY
Definition movetypes.qh:147
float move_movetype
Definition movetypes.qh:80
const int MOVETYPE_NOCLIP
Definition movetypes.qh:141
const int MOVETYPE_FLY
Definition movetypes.qh:138
@ STATUSEFFECT_REMOVE_CLEAR
Effect is being forcibly removed without calling any additional mechanics.
Definition all.qh:30
@ STATUSEFFECT_REMOVE_NORMAL
Effect is being removed by a function, calls regular removal mechanics.
Definition all.qh:28
@ STATUSEFFECT_REMOVE_TIMEOUT
Definition all.qh:29
@ RACE_NET_CHECKPOINT_CLEAR
Definition net_linked.qh:15
void sv_notice_join(entity _to)
Definition net_notice.qc:14
var void func_null()
string string_null
Definition nil.qh:9
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 CPID_PREVENT_JOIN
Definition all.inc:736
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 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:694
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
Definition all.qc:1464
#define APP_TEAM_NUM(num, prefix)
Definition all.qh:88
#define TRANSMUTE(cname, this,...)
Definition oo.qh:139
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:66
#define STATIC_METHOD(cname, name, prototype)
Definition oo.qh:283
void PlayerStats_GameReport_FinalizePlayer(entity p)
void PlayerStats_GameReport_AddPlayer(entity e)
void PlayerStats_GameReport_AddEvent(string event_id)
const string PLAYERSTATS_ALIVETIME
#define PlayerStats_GameReport_Event_Player(ent, eventid, val)
void Portal_ClearAll(entity own)
Definition portals.qc:586
#define NULL
Definition post.qh:14
#define world
Definition post.qh:15
#define error
Definition pre.qh:6
#define setcolor
Definition pre.qh:11
float DEAD_DYING
Definition progsdefs.qc:275
float parm1
Definition progsdefs.qc:45
float button2
Definition progsdefs.qc:156
float button0
Definition progsdefs.qc:154
entity msg_entity
Definition progsdefs.qc:63
vector view_ofs
Definition progsdefs.qc:151
float DEAD_NO
Definition progsdefs.qc:274
float fixangle
Definition progsdefs.qc:160
float deadflag
Definition progsdefs.qc:149
float DEAD_RESPAWNABLE
Definition progsdefs.qc:277
float impulse
Definition progsdefs.qc:158
float DEAD_RESPAWNING
Definition progsdefs.qc:278
string weaponmodel
Definition progsdefs.qc:140
float dmg_save
Definition progsdefs.qc:199
float DEAD_DEAD
Definition progsdefs.qc:276
float armortype
Definition progsdefs.qc:178
entity dmg_inflictor
Definition progsdefs.qc:200
#define stuffcmd(cl,...)
Definition progsdefs.qh:23
float scale
Definition projectile.qc:14
q3compat
Definition quake3.qc:59
bool autocvar_sv_q3compat_changehitbox
Definition quake3.qh:7
int fragsfilter_cnt
Definition quake3.qh:11
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
#define RandomSelection_AddEnt(e, weight, priority)
Definition random.qh:14
entity RandomSelection_chosen_ent
Definition random.qh:5
ERASEABLE ACCUMULATE void Registry_send_all()
Definition registry.qh:205
float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
Sets the player's score to the score parameter.
Definition scores.qc:381
bool PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
Definition scores.qc:286
#define AVAILABLE_TEAMS
Number of teams that exist currently.
void W_NextWeapon(entity this, int list,.entity weaponentity)
Goto next weapon.
Definition selection.qc:310
#define w_getbestweapon(ent, wepent)
Definition selection.qh:23
bool autocvar_g_weaponswitch_debug
Definition selection.qh:7
#define setthink(e, f)
vector
Definition self.qh:96
int Campaign_GetLevelNum()
Definition campaign.qc:44
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:26
#define CHAT_NOSPECTATORS()
Definition chat.qh:30
void player_powerups_remove_all(entity this, bool allow_poweroff_sound)
Definition client.qc:1541
int player_getspecies(entity this)
Definition client.qc:444
bool SpectateSet(entity this)
Definition client.qc:1885
void play_countdown(entity this, float finished, Sound samp)
Definition client.qc:1532
entity chatbubbleentity
Definition client.qc:1268
void SpectateCopy(entity this, entity spectatee)
Definition client.qc:1799
void WriteSpectators(entity player, entity to)
Definition client.qc:114
int CountSpectators(entity player, entity to)
Definition client.qc:100
float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
Definition client.qc:1637
void GiveWarmupResources(entity this)
Definition client.qc:568
entity SelectObservePoint(entity this)
Definition client.qc:250
bool ShowTeamSelection(entity this)
Definition client.qc:2062
int GetPlayerLimit()
Definition client.qc:2155
bool SpectateUpdate(entity this)
Definition client.qc:1867
void DrownPlayer(entity this)
Definition client.qc:2757
void GetPressedKeys(entity this)
Definition client.qc:1767
void PlayerUseKey(entity this)
Definition client.qc:2612
float autocvar_g_maxping
Definition client.qc:2257
void ClientData_Detach(entity this)
Definition client.qc:179
void player_powerups(entity this)
Definition client.qc:1557
void SetNewParms()
Definition client.qc:960
void SendWelcomeMessage(entity this, int msg_type)
Definition client.qc:1077
bool zoomstate_set
Definition client.qc:1756
void UpdateChatBubble(entity this)
Definition client.qc:1377
void FixClientCvars(entity e)
Definition client.qc:999
bool PlayerInIDList(entity p, string idlist)
Definition client.qc:1038
bool would_spectate
Definition client.qc:2491
void SetZoomState(entity this, float newzoom)
Definition client.qc:1757
float model_randomizer
Definition client.qc:453
string CheckPlayerModel(string plyermodel)
Definition client.qc:208
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition client.qc:261
void player_regen(entity this)
Definition client.qc:1688
float last_vehiclecheck
Definition client.qc:2674
void Player_Physics(entity this)
Definition client.qc:2790
bool SpectateNext(entity this)
Definition client.qc:1987
void ChatBubbleThink(entity this)
Definition client.qc:1350
void respawn(entity this)
Definition client.qc:1502
void PutPlayerInServer(entity this)
Definition client.qc:580
void calculate_player_respawn_time(entity this)
Definition client.qc:1399
float CalcRot(float current, float stable, float rotfactor, float rotframetime)
Definition client.qc:1647
void ClientInit_CheckUpdate(entity this)
Definition client.qc:931
bool PlayerThink(entity this)
Definition client.qc:2336
bool ClientInit_SendEntity(entity this, entity to, int sf)
Definition client.qc:895
void ObserverOrSpectatorThink(entity this)
Definition client.qc:2493
bool PlayerInIPList(entity p, string iplist)
Definition client.qc:1029
bool queuePlayer(entity this, int team_index)
Definition client.qc:2209
void ShowRespawnCountdown(entity this)
Definition client.qc:2043
int nJoinAllowed(entity this)
Determines how many player slots are free.
Definition client.qc:2171
void PM_UpdateButtons(entity this, entity store)
Definition client.qc:3087
void SetSpectatee_status(entity this, int spectatee_num)
Definition client.qc:1904
bool Spectate(entity this, entity pl)
Definition client.qc:1977
bool joinAllowed(entity this, int team_index)
Definition client.qc:2258
bool dualwielding_prev
Definition client.qc:2335
bool findinlist_abbrev(string tofind, string list)
Definition client.qc:1015
void FixPlayermodel(entity player)
Definition client.qc:454
bool SpectatePrev(entity this)
Definition client.qc:2001
void PlayerFrame(entity this)
Definition client.qc:2844
bool PlayerInList(entity player, string list)
Definition client.qc:1047
void ClientInit_Spawn()
Definition client.qc:946
void ClientData_Attach(entity this)
Definition client.qc:172
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:1657
void DecodeLevelParms(entity this)
Definition client.qc:986
void show_entnum(entity this)
Definition client.qc:2309
string shootfromfixedorigin
Definition client.qc:1267
void Join(entity this, bool queued_join)
it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
Definition client.qc:2074
void ClientInit_misc(entity this)
Definition client.qc:907
void SetSpectatee(entity this, entity spectatee)
Definition client.qc:1939
void setplayermodel(entity e, string modelname)
Definition client.qc:241
string FallbackPlayerModel
Definition client.qc:207
void ClientData_Touch(entity e, bool to_spectators_too)
Definition client.qc:185
bool ClientData_Send(entity this, entity to, int sf)
Definition client.qc:128
int killcount
Definition client.qh:315
string autocvar_sv_quickmenu_file
Definition client.qh:54
float autocvar_sv_foginterval
Definition client.qh:36
float autocvar_sv_maxidle
Definition client.qh:37
float respawn_time_max
Definition client.qh:322
string autocvar_g_xonoticversion
Definition client.qh:46
int respawn_flags
Definition client.qh:320
const int RESPAWN_SILENT
Definition client.qh:327
bool autocvar_g_respawn_ghosts
Definition client.qh:28
bool autocvar_g_forced_respawn
Definition client.qh:43
string weaponorder_byimpulse
Definition client.qh:62
float autocvar_g_balance_pause_health_rot_spawn
Definition client.qh:15
string autocvar_sv_motd
Definition client.qh:52
bool autocvar_sv_servermodelsonly
Definition client.qh:55
int playerid
Definition client.qh:82
bool autocvar_sv_teamnagger
Definition client.qh:58
string autocvar_g_mutatormsg
Definition client.qh:35
bool autocvar_sv_spectate
Definition client.qh:57
#define INDEPENDENT_PLAYERS
Definition client.qh:311
bool autocvar_g_fullbrightplayers
Definition client.qh:17
const int MIN_SPEC_TIME
Definition client.qh:403
IntrusiveList g_observepoints
Definition client.qh:371
int team_selected
Definition client.qh:75
bool autocvar_g_botclip_collisions
Definition client.qh:16
float autocvar_sv_player_scale
Definition client.qh:59
bool just_joined
Definition client.qh:76
float autocvar_g_balance_pause_armor_rot_spawn
Definition client.qh:12
int autocvar_sv_maxidle_minplayers
Definition client.qh:38
IntrusiveList g_initforplayer
Definition client.qh:370
int autocvar_g_balance_armor_start
Definition client.qh:11
float autocvar_sv_maxidle_playertospectator
Definition client.qh:39
float autocvar_g_balance_pause_fuel_rot_spawn
Definition client.qh:13
bool autocvar__notarget
Definition client.qh:9
float autocvar_g_maxplayers_spectator_blocktime
Definition client.qh:45
#define IS_INDEPENDENT_PLAYER(e)
Definition client.qh:312
string autocvar_hostname
Definition client.qh:50
const int RESPAWN_DENY
Definition client.qh:328
float jointime
Definition client.qh:66
float pauserotfuel_finished
Definition client.qh:340
float autocvar_g_respawn_ghosts_time
Definition client.qh:31
bool zoomstate
Definition client.qh:72
float respawn_time
Definition client.qh:321
float autocvar_g_respawn_ghosts_alpha
Definition client.qh:29
bool autocvar_sv_maxidle_slots_countbots
Definition client.qh:42
int autocvar_spawn_debug
Definition client.qh:51
bool autocvar_g_playerclip_collisions
Definition client.qh:18
bool autocvar_g_nodepthtestplayers
Definition client.qh:34
float pauseregen_finished
Definition client.qh:337
#define GAMETYPE_DEFAULTED_SETTING(str)
Definition client.qh:347
#define MAKE_INDEPENDENT_PLAYER(e)
Definition client.qh:313
int autocvar_g_maxplayers
Definition client.qh:44
float pauserothealth_finished
Definition client.qh:338
float autocvar_g_respawn_ghosts_speed
Definition client.qh:32
const int SVC_SETVIEW
Definition client.qh:334
float respawn_countdown
Definition client.qh:324
float autocvar_g_player_brightness
Definition client.qh:20
float autocvar_g_player_damageforcescale
Definition client.qh:21
float pauserotarmor_finished
Definition client.qh:339
float spectatortime
Definition client.qh:330
const int RESPAWN_FORCE
Definition client.qh:326
int autocvar_sv_name_maxlength
Definition client.qh:53
float autocvar_gameversion
Definition client.qh:47
int autocvar_sv_maxidle_slots
Definition client.qh:41
float autocvar_g_respawn_ghosts_fadetime
Definition client.qh:30
bool autocvar_sv_show_entnum
Definition client.qh:10
float autocvar_g_balance_pause_health_regen_spawn
Definition client.qh:14
entity clientdata
Definition client.qh:64
bool autocvar_sv_maxidle_alsokickspectators
Definition client.qh:40
float alivetime_start
Definition client.qh:68
bool autocvar_sv_showspectators
Definition client.qh:56
int autocvar_sv_timeout_number
Definition common.qh:8
vector lastV_angle
Definition common.qh:62
const float TIMEOUT_ACTIVE
Definition common.qh:49
void ReadyCount()
Definition vote.qc:553
void VoteCount(float first_count)
Definition vote.qc:220
void ReadyRestart(bool forceWarmupEnd)
Definition vote.qc:526
string autocvar_sv_vote_master_ids
Definition vote.qh:14
bool vote_master
Definition vote.qh:51
int vote_called
Definition vote.qh:45
void RemoveGrapplingHooks(entity pl)
Definition hook.qc:30
void ImpulseCommands(entity this)
Definition impulse.qc:371
int int number
Definition impulse.qc:89
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:440
float autocvar_g_balance_superweapons_time
Definition items.qh:7
float max_armorvalue
Definition items.qh:26
bool dropclient_schedule(entity this)
Schedules dropclient for a player and returns true; if dropclient is already scheduled (for that play...
Definition main.qc:44
bool autocvar_sv_autopause
Definition main.qh:19
int autocvar_g_balance_contents_playerdamage_drowning
Definition main.qh:5
vector oldvelocity
Definition main.qh:42
float autocvar_g_balance_contents_drowndelay
Definition main.qh:4
float autocvar_g_balance_contents_damagerate
Definition main.qh:3
bool iscreature
Definition main.qh:46
@ MUT_SPECPREV_RETURN
Definition events.qh:985
@ MUT_SPECPREV_CONTINUE
Definition events.qh:984
@ MUT_SPECPREV_FOUND
Definition events.qh:986
void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition player.qc:234
void player_anim(entity this)
Definition player.qc:153
bool PlayerHeal(entity targ, entity inflictor, float amount, float limit)
Definition player.qc:615
void player_setupanimsformodel(entity this)
Definition player.qc:146
void CopyBody(entity this, float keepvelocity)
Definition player.qc:64
string autocvar_sv_defaultplayermodel_blue
Definition player.qh:14
int autocvar_sv_defaultplayerskin_pink
Definition player.qh:9
float score_frame_dmgtaken
Definition player.qh:26
float score_frame_dmg
Definition player.qh:25
int autocvar_sv_defaultplayerskin_yellow
Definition player.qh:11
string autocvar_sv_defaultplayermodel_yellow
Definition player.qh:17
string autocvar_sv_defaultplayermodel_red
Definition player.qh:16
string autocvar_sv_defaultplayercolors
Definition player.qh:12
bool autocvar_sv_defaultcharacter
Definition player.qh:6
int autocvar_sv_defaultplayerskin_blue
Definition player.qh:8
float autocvar_g_spawnshieldtime
Definition player.qh:3
int autocvar_sv_defaultplayerskin_red
Definition player.qh:10
int autocvar_sv_defaultplayerskin
Definition player.qh:18
string autocvar_sv_defaultplayermodel
Definition player.qh:13
string autocvar_sv_defaultplayermodel_pink
Definition player.qh:15
bool autocvar_sv_defaultcharacterskin
Definition player.qh:7
void race_SendNextCheckpoint(entity e, float spec)
Definition race.qc:208
bool autocvar_g_allow_checkpoints
Definition race.qh:3
float race_completed
Definition race.qh:24
int g_race_qualifying
Definition race.qh:11
bool SpawnEvent_Send(entity this, entity to, int sf)
entity SelectSpawnPoint(entity this, bool anypoint)
bool W_DualWielding(entity player)
Definition common.qc:20
float fade_time
Definition common.qh:23
void WarpZone_PlayerPhysics_FixVAngle(entity this)
Definition server.qc:817
const float VOL_BASEVOICE
Definition sound.qh:37
const int CH_TRIGGER_SINGLE
Definition sound.qh:13
const int CH_PLAYER_SINGLE
Definition sound.qh:21
const float VOL_BASE
Definition sound.qh:36
const int CH_INFO
Definition sound.qh:6
const int CH_VOICE
Definition sound.qh:10
const float ATTEN_NORM
Definition sound.qh:30
#define sound(e, c, s, v, a)
Definition sound.qh:52
const int CH_PLAYER
Definition sound.qh:20
void play2(entity e, string filename)
Definition all.qc:116
void stopsound(entity e, int chan)
Definition all.qc:109
#define SND(id)
Definition all.qh:35
entity spawnpoint_targ
Definition spawnpoint.qh:4
void PlayerState_attach(entity this)
Definition state.qc:12
void bot_clientconnect(entity this)
Definition bot.qc:469
void ClientState_detach(entity this)
Definition state.qc:73
void PlayerState_detach(entity this)
Definition state.qc:22
void DecodeLevelParms(entity this)
Definition client.qc:986
#define CS_CVAR(this)
Definition state.qh:51
#define PS(this)
Definition state.qh:18
ClientState CS(Client this)
Definition state.qh:47
void StatusEffects_remove(StatusEffect this, entity actor, int removal_type)
float StatusEffects_gettime(StatusEffect this, entity actor)
bool StatusEffects_active(StatusEffect this, entity actor)
void StatusEffects_apply(StatusEffect this, entity actor, float eff_time, int eff_flags)
void ONREMOVE(entity this)
#define strfree(this)
Definition string.qh:57
ERASEABLE int vercmp(string v1, string v2)
Definition string.qh:563
#define strcpy(this, s)
Definition string.qh:51
ERASEABLE bool isInvisibleString(string s)
Definition string.qh:392
void SUB_SetFade(entity ent, float vanish_time, float fading_time)
Definition subs.qc:77
const int DAMAGE_NO
Definition subs.qh:79
const int DAMAGE_AIM
Definition subs.qh:81
float takedamage
Definition subs.qh:78
entity enemy
Definition sv_ctf.qh:152
bool MinigameImpulse(entity this, int imp)
void part_minigame(entity player)
void monsters_setstatus(entity this)
bool monster_attack
indicates whether an entity can be attacked by monsters
float spawn_time
delay monster thinking until spawn animation has completed
IntrusiveList g_monster_targets
int oldskin
float g_nexball_meter_period
Definition sv_nexball.qh:53
float GetResourceLimit(entity e, Resource res_type)
Returns the maximum amount of the given resource.
Header file that describes the resource system.
float autocvar_g_balance_armor_rotlinear
int autocvar_g_balance_fuel_rotstable
float autocvar_g_balance_fuel_regen
float autocvar_g_balance_armor_regen
float autocvar_g_balance_fuel_regenlinear
float autocvar_g_balance_health_rotstable
float autocvar_g_balance_health_regenstable
int autocvar_g_balance_armor_regenstable
float autocvar_g_balance_health_rotlinear
float autocvar_g_balance_health_regenlinear
float autocvar_g_balance_fuel_rotlinear
int autocvar_g_balance_fuel_regenstable
float autocvar_g_balance_armor_rot
float autocvar_g_balance_health_regen
int autocvar_g_balance_armor_rotstable
float autocvar_g_balance_health_rot
float autocvar_g_balance_fuel_rot
float autocvar_g_balance_armor_regenlinear
#define INGAME(it)
Definition sv_rules.qh:24
#define GameRules_scoring_add(client, fld, value)
Definition sv_rules.qh:85
void vehicles_enter(entity pl, entity veh)
void vehicles_exit(entity vehic, bool eject)
const float vehicle_shield
If ent is player this is 0..100 indicating precentage of shield left on vehicle. If ent is vehicle,...
const float vehicle_reload2
If ent is player this is 0..100 indicating percentage of secondary reload status. If ent is vehicle,...
const float vehicle_ammo1
If ent is player this is 0..100 indicating percentage of primary ammo left UNLESS value is already st...
const float vehicle_health
If ent is player this is 0..100 indicating precentage of health left on vehicle. Vehicle's value is t...
const float vehicle_energy
If ent is player this is 0..100 indicating precentage of energy left on vehicle. If ent is vehicle,...
const int VHEF_NORMAL
User pressed exit key.
const int VHEF_RELEASE
Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (n...
entity vehicle
const float vehicle_ammo2
If ent is player this is 0..100 indicating percentage of secondary ammo left. If ent is vehicle,...
float autocvar_g_vehicles_enter_radius
const float vehicle_reload1
If ent is player this is 0..100 indicating percentage of primary reload status. If ent is vehicle,...
entity swampslug
Definition swamp.qh:7
float swamp_interval
Definition swamp.qh:9
IntrusiveList g_swamped
Definition swamp.qh:4
bool QueuedPlayersReady(entity this, bool checkspecificteam)
Returns true when enough players are queued that the next will join directly to the only available te...
Definition teamplay.qc:269
bool TeamBalance_QueuedPlayersTagIn(entity ignore)
Joins queued player(s) to team(s) with a shortage, this should be more robust than only replacing the...
Definition teamplay.qc:765
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition teamplay.qc:599
void TeamBalance_RemoveExcessPlayers(entity ignore)
Definition teamplay.qc:702
bool QueueNeeded(entity client, int team_index)
Definition teamplay.qc:1335
void Player_DetermineForcedTeam(entity player)
Determines the forced team of the player using current global config.
Definition teamplay.qc:379
void TeamBalance_JoinBestTeam(entity player)
Assigns the given player to a team that will make the game most balanced.
Definition teamplay.qc:454
bool Player_HasRealForcedTeam(entity player)
Returns whether player has real forced team.
Definition teamplay.qc:344
bool SetPlayerTeam(entity player, int team_index, int type)
Sets the team of the player.
Definition teamplay.qc:286
int Player_GetForcedTeamIndex(entity player)
Returns the index of the forced team of the given player.
Definition teamplay.qc:349
int TeamBalance_GetAllowedTeams(entity balance)
Returns the bitmask of allowed teams.
Definition teamplay.qc:612
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:490
bool TeamBalance_AreEqual(entity ignore, bool would_leave)
Definition teamplay.qc:656
int TeamBalance_FindBestTeam(entity balance, entity player, bool ignore_player)
Finds the team that will make the game most balanced if the player joins it.
Definition teamplay.qc:964
string autocvar_g_forced_team_otherwise
Definition teamplay.qh:16
@ TEAM_FORCE_SPECTATOR
Force the player to spectator team.
Definition teamplay.qh:152
bool autocvar_g_balance_teams_remove
Definition teamplay.qh:10
@ TEAM_CHANGE_AUTO
The team was selected by autobalance.
Definition teamplay.qh:129
@ TEAM_CHANGE_MANUAL
Player has manually selected their team.
Definition teamplay.qh:130
@ TEAM_CHANGE_SPECTATOR
Player is joining spectators. //TODO: Remove?
Definition teamplay.qh:131
bool autocvar_g_balance_teams
Definition teamplay.qh:7
bool lockteams
Definition teamplay.qh:20
#define SAME_TEAM(a, b)
Definition teams.qh:241
const int NUM_TEAM_2
Definition teams.qh:14
const int NUM_TEAM_4
Definition teams.qh:16
const int NUM_TEAM_3
Definition teams.qh:15
int Team_IndexToTeam(int index)
Converts team index into team value.
Definition teams.qh:169
bool teamplay
Definition teams.qh:59
const int NUM_TEAM_1
Definition teams.qh:13
const int TELEPORT_NORMAL
float teleportable
const int TELEPORT_SIMPLE
void WarpZone_crosshair_trace_plusvisibletriggers(entity pl)
Definition tracing.qc:559
float autocvar_g_trueaim_minrange
Definition tracing.qh:17
float autocvar_g_ballistics_density_player
Definition tracing.qh:7
#define IS_OBSERVER(v)
Definition utils.qh:11
#define IS_SPEC(v)
Definition utils.qh:10
const string STR_PLAYER
Definition utils.qh:5
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define IS_VEHICLE(v)
Definition utils.qh:24
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
#define vlen2(v)
Definition vector.qh:4
#define vec2(...)
Definition vector.qh:95
const int VHF_MULTISLOT
Vehicle has multiple player slots.
Definition vehicle.qh:106
int cursor_active
Definition view.qh:112
float dmg_take
Definition view.qh:123
void target_voicescript_next(entity pl)
void target_voicescript_clear(entity pl)
Definition voicescript.qc:8
void W_Vortex_Charge(entity actor,.entity weaponentity, float dt)
Definition vortex.qc:174
float vortex_charge_rottime
Definition vortex.qh:92
bool waypointeditor_enabled
Definition waypoints.qh:3
entity wp_aimed
Definition waypoints.qh:47
void WaypointSprite_PlayerGone(entity this)
void WaypointSprite_PlayerDead(entity this)
void WaypointSprite_UpdateHealth(entity e, float f)
entity waypointsprite_attachedforcarrier
void W_PROP_reload(int chan, entity to)
Definition all.qh:110
#define WEPSET(id)
Definition all.qh:47
string autocvar_g_shootfromfixedorigin
Definition all.qh:442
entity exteriorweaponentity
Definition all.qh:401
#define WEP_CVAR(wep, name)
Definition all.qh:337
WepSet WEPSET_SUPERWEAPONS
Definition all.qh:345
vector spawnorigin
Definition all.qh:416
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
const int WEP_FLAG_RELOADABLE
Definition weapon.qh:259
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
void W_WeaponFrame(Player actor,.entity weaponentity)
void CL_SpawnWeaponentity(entity actor,.entity weaponentity)
Spawn weaponentity for client.
void W_ResetGunAlign(entity player, int preferred_alignment)
const int W_TICSPERFRAME
float weapon_load[REGISTRY_MAX(Weapons)]
float vortex_charge
Definition wepent.qh:6
Weapon m_weapon
Definition wepent.qh:26
WepSet start_weapons
Definition world.qh:80
float warmup_start_ammo_cells
Definition world.qh:105
string clientstuff
Definition world.qh:61
#define WARMUP_START_WEAPONS
Definition world.qh:101
entity random_start_ammo
Entity that contains amount of ammo to give with random start weapons.
Definition world.qh:95
string autocvar_g_random_start_weapons
Holds a list of possible random start weapons.
Definition world.qh:92
bool autocvar_g_jetpack
Definition world.qh:8
float start_ammo_shells
Definition world.qh:84
float warmup_start_ammo_rockets
Definition world.qh:104
float warmup_start_ammo_shells
Definition world.qh:102
float start_ammo_fuel
Definition world.qh:88
int start_items
Definition world.qh:83
bool autocvar_sv_mapformat_is_quake3
Definition world.qh:32
int random_start_weapons_count
Number of random start weapons to give to players.
Definition world.qh:90
float warmup_start_ammo_nails
Definition world.qh:103
float default_weapon_alpha
Definition world.qh:73
WepSet g_weaponarena_weapons
Definition world.qh:76
float default_player_alpha
Definition world.qh:72
float start_ammo_cells
Definition world.qh:87
string cache_lastmutatormsg
Definition world.qh:70
float g_weaponarena
Definition world.qh:75
float warmup_start_health
Definition world.qh:107
bool autocvar_sv_dedicated
Definition world.qh:41
float start_ammo_rockets
Definition world.qh:86
float g_weapon_stay
Definition world.qh:109
float start_armorvalue
Definition world.qh:97
int autocvar_g_warmup
Definition world.qh:9
bool observe_blocked_if_eliminated
Definition world.qh:160
float warmup_start_ammo_fuel
Definition world.qh:106
string g_weaponarena_list
Definition world.qh:78
float start_health
Definition world.qh:96
bool sv_ready_restart_after_countdown
Definition world.qh:116
float warmup_start_armorvalue
Definition world.qh:108
float start_ammo_nails
Definition world.qh:85
string cache_mutatormsg
Definition world.qh:69