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 // bones_was_here: this MUST succeed, PutClientInServer->PutPlayerInServer
593 // can't handle failures (must only be called if they'd succeed).
594 if (this.bot_forced_team)
596 else if (this.team <= 0)
597 {
598 if (this.wants_join > 0)
600 else
602 }
603 }
604
605 entity spot = SelectSpawnPoint(this, false);
606 if (!spot)
607 {
608 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
609 return; // spawn failed
610 }
611
612 TRANSMUTE(Player, this);
613
614 this.iscreature = true;
616 if(!this.damagedbycontents)
618 this.damagedbycontents = true;
620 this.solid = SOLID_SLIDEBOX;
626 this.frags = FRAGS_PLAYER;
630 this.flags |= FL_NOTARGET;
631 this.takedamage = DAMAGE_AIM;
633
634 if (warmup_stage)
636 else
637 {
638 SetResource(this, RES_SHELLS, start_ammo_shells);
639 SetResource(this, RES_BULLETS, start_ammo_nails);
640 SetResource(this, RES_ROCKETS, start_ammo_rockets);
641 SetResource(this, RES_CELLS, start_ammo_cells);
642 SetResource(this, RES_FUEL, start_ammo_fuel);
643 SetResource(this, RES_HEALTH, start_health);
645 STAT(WEAPONS, this) = start_weapons;
646 if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false)
647 {
650 }
651 }
652 SetSpectatee_status(this, 0);
653
654 PS(this).dual_weapons = '0 0 0';
655
656 if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
657 StatusEffects_apply(STATUSEFFECT_Superweapon, this, time + autocvar_g_balance_superweapons_time, 0);
658
659 this.items = start_items;
660
661 float shieldtime = time + autocvar_g_spawnshieldtime;
662
668 {
669 float f = game_starttime - time;
670 shieldtime += f;
671 this.pauserotarmor_finished += f;
672 this.pauserothealth_finished += f;
673 this.pauseregen_finished += f;
674 }
675
676 StatusEffects_apply(STATUSEFFECT_SpawnShield, this, shieldtime, 0);
677
679 this.death_time = 0;
680 this.respawn_flags = 0;
681 this.respawn_time = 0;
682 STAT(RESPAWN_TIME, this) = 0;
684 ? 0.8125 // DP model scaling uses 1/16 accuracy and 13/16 is closest to 56/69
686 this.fade_time = 0;
687 this.pain_finished = 0;
688 this.pushltime = 0;
689 setthink(this, func_null); // players have no think function
690 this.nextthink = 0;
691 this.dmg_team = 0;
692 this.spawn_time = time;
693
694 PS(this).ballistics_density = autocvar_g_ballistics_density_player;
695
696 this.deadflag = DEAD_NO;
697
698 this.angles = spot.angles;
699 this.angles_z = 0; // never spawn tilted even if the spot says to
700 if (IS_BOT_CLIENT(this))
701 {
702 this.v_angle = this.angles;
703 bot_aim_reset(this);
704 }
705 this.fixangle = true; // turn this way immediately
706 this.oldvelocity = this.velocity = '0 0 0';
707 this.avelocity = '0 0 0';
708 this.punchangle = '0 0 0';
709 this.punchvector = '0 0 0';
710
711 STAT(AIR_FINISHED, this) = 0;
714
715 entity spawnevent = new_pure(spawnevent);
716 spawnevent.owner = this;
717 Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
718
719 // Cut off any still running player sounds.
721
722 this.model = "";
723 FixPlayermodel(this);
724 this.drawonlytoclient = NULL;
725
726 this.viewloc = NULL;
727
728 for(int slot = 0; slot < MAX_AXH; ++slot)
729 {
730 entity axh = this.(AuxiliaryXhair[slot]);
731 this.(AuxiliaryXhair[slot]) = NULL;
732
733 if(axh.owner == this && axh != NULL && !wasfreed(axh))
734 delete(axh);
735 }
736
737 this.spawnpoint_targ = NULL;
738
739 UNSET_DUCKED(this);
740 this.view_ofs = STAT(PL_VIEW_OFS, this);
741 setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
742 this.spawnorigin = spot.origin;
743 setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
744 // don't reset back to last position, even if new position is stuck in solid
745 this.oldorigin = this.origin;
746 if(this.conveyor)
747 IL_REMOVE(g_conveyed, this);
748 this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
749 if(this.swampslug)
750 IL_REMOVE(g_swamped, this);
751 this.swampslug = NULL;
752 this.swamp_interval = 0;
753 if(this.ladder_entity)
754 IL_REMOVE(g_ladderents, this);
755 this.ladder_entity = NULL;
756 IL_EACH(g_counters, it.realowner == this,
757 {
758 delete(it);
759 });
760 STAT(HUD, this) = HUD_NORMAL;
761
762 this.event_damage = PlayerDamage;
763 this.event_heal = PlayerHeal;
764
765 this.draggable = func_null;
766
767 if(!this.bot_attack)
768 IL_PUSH(g_bot_targets, this);
769 this.bot_attack = true;
770 if(!this.monster_attack)
772 this.monster_attack = true;
773 navigation_dynamicgoal_init(this, false);
774
776
777 // player was spectator
778 if (CS(this).killcount == FRAGS_SPECTATOR)
779 {
780 PlayerScore_Clear(this);
781 CS(this).killcount = 0;
782 CS(this).startplaytime = time;
783 }
784
785 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
786 {
787 .entity weaponentity = weaponentities[slot];
788 CL_SpawnWeaponentity(this, weaponentity);
789 }
791 this.colormod = '1 1 1' * autocvar_g_player_brightness;
793
794 this.speedrunning = false;
795
796 this.counter_cnt = 0;
797 this.fragsfilter_cnt = 0;
798
800
801 // reset fields the weapons may use
802 FOREACH(Weapons, true,
803 {
804 it.wr_resetplayer(it, this);
805 // reload all reloadable weapons
806 if (it.spawnflags & WEP_FLAG_RELOADABLE)
807 {
808 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
809 {
810 .entity weaponentity = weaponentities[slot];
811 this.(weaponentity).weapon_load[it.m_id] = it.reloading_ammo;
812 }
813 }
814 });
815
816 MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
817 {
818 string s = spot.target;
819 if(g_assault || g_race) // TODO: make targeting work in assault & race without this hack
820 spot.target = string_null;
821 SUB_UseTargets(spot, this, NULL);
822 if(g_assault || g_race)
823 spot.target = s;
824 }
825
827 {
828 sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n"));
829 delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
830 }
831
832 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
833 {
834 .entity weaponentity = weaponentities[slot];
835 entity w_ent = this.(weaponentity);
836 if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
837 w_ent.m_switchweapon = w_getbestweapon(this, weaponentity);
838 else
839 w_ent.m_switchweapon = WEP_Null;
840 w_ent.m_weapon = WEP_Null;
841 w_ent.weaponname = "";
842 w_ent.m_switchingweapon = WEP_Null;
843 w_ent.cnt = -1;
844 }
845
846 MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
847
848 if (CS(this).impulse) ImpulseCommands(this);
849
850 W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
851 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
852 {
853 .entity weaponentity = weaponentities[slot];
854 W_WeaponFrame(this, weaponentity);
855 }
856
857 if (!warmup_stage && !this.alivetime_start)
859
860 antilag_clear(this, CS(this));
861
862 if (warmup_stage)
863 ReadyCount();
864}
865
868{
869 if (IS_REAL_CLIENT(this))
870 {
871 msg_entity = this;
873 WriteEntity(MSG_ONE, this);
874 }
875 if (game_stopped)
876 TRANSMUTE(Observer, this);
877
878 bool use_spawnpoint = (!this.enemy); // check this.enemy here since SetSpectatee will clear it
879 SetSpectatee(this, NULL);
880
881 // reset player keys
882 if(PS(this))
883 PS(this).itemkeys = 0;
884
886
887 if (IS_OBSERVER(this))
888 PutObserverInServer(this, false, use_spawnpoint);
889 else if (IS_PLAYER(this))
890 PutPlayerInServer(this);
891
893}
894
895// TODO do we need all these fields, or should we stop autodetecting runtime
896// changes and just have a console command to update this?
897bool ClientInit_SendEntity(entity this, entity to, int sf)
898{
899 WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
900 return = true;
901 msg_entity = to;
902 // MSG_INIT replacement
903 // TODO: make easier to use
906 ClientInit_misc(this);
907 MUTATOR_CALLHOOK(Ent_Init);
908}
910{
911 int channel = MSG_ONE;
912 WriteHeader(channel, ENT_CLIENT_INIT);
913 WriteByte(channel, g_nexball_meter_period * 32);
914 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
915 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
916 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
917 WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
918 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
919 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
920 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
921 WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
922
923 WriteString(channel, world.fog);
924 WriteByte(channel, autocvar_sv_foginterval && world.fog != ""); // only forced if necessary
925
926 WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
927 WriteByte(channel, this.cnt * 255.0); // g_balance_damagepush_speedfactor
928 WriteByte(channel, serverflags);
930}
931
933{
934 this.nextthink = time;
936 {
938 this.SendFlags |= 1;
939 }
941 {
943 this.SendFlags |= 1;
944 }
945}
946
948{
949 entity e = new_pure(clientinit);
952
954}
955
956/*
957=============
958SetNewParms
959=============
960*/
962{
963 // initialize parms for a new player
964 parm1 = -(86400 * 366);
965
967}
968
969/*
970=============
971SetChangeParms
972=============
973*/
975{
976 // save parms for level change
977 parm1 = CS(this).parm_idlesince - time;
978
980}
981
982/*
983=============
984DecodeLevelParms
985=============
986*/
988{
989 // load parms
990 CS(this).parm_idlesince = parm1;
991 if (CS(this).parm_idlesince == -(86400 * 366))
992 CS(this).parm_idlesince = time;
993
994 // whatever happens, allow 60 seconds of idling directly after connect for map loading
995 CS(this).parm_idlesince = max(CS(this).parm_idlesince, time - autocvar_sv_maxidle + 60);
996
998}
999
1001{
1002 // send prediction settings to the client
1003 if(autocvar_g_antilag == 3) // client side hitscan
1004 stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
1005 if(autocvar_sv_gentle)
1006 stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
1007
1008 stuffcmd(e, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min));
1009 stuffcmd(e, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max));
1010
1011 stuffcmd(e, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin));
1012
1014}
1015
1016bool findinlist_abbrev(string tofind, string list)
1017{
1018 if(list == "" || tofind == "")
1019 return false; // empty list or search, just return
1020
1021 // this function allows abbreviated strings!
1022 FOREACH_WORD(list, it != "" && it == substring(tofind, 0, strlen(it)),
1023 {
1024 return true;
1025 });
1026
1027 return false;
1028}
1029
1030bool PlayerInIPList(entity p, string iplist)
1031{
1032 // some safety checks (never allow local?)
1033 if(p.netaddress == "local" || p.netaddress == "" || !IS_REAL_CLIENT(p))
1034 return false;
1035
1036 return findinlist_abbrev(p.netaddress, iplist);
1037}
1038
1039bool PlayerInIDList(entity p, string idlist)
1040{
1041 // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this
1042 if(!p.crypto_idfp)
1043 return false;
1044
1045 return findinlist_abbrev(p.crypto_idfp, idlist);
1046}
1047
1048bool PlayerInList(entity player, string list)
1049{
1050 if (list == "")
1051 return false;
1052 return boolean(PlayerInIDList(player, list) || PlayerInIPList(player, list));
1053}
1054
1055#ifdef DP_EXT_PRECONNECT
1056/*
1057=============
1058ClientPreConnect
1059
1060Called once (not at each match start) when a client begins a connection to the server
1061=============
1062*/
1063void ClientPreConnect(entity this)
1064{
1066 {
1067 GameLogEcho(sprintf(":connect:%d:%d:%s",
1068 this.playerid,
1069 etof(this),
1070 IS_REAL_CLIENT(this) ? this.netaddress : "bot"));
1071 }
1072}
1073#endif
1074
1075// NOTE csqc uses the active mutators list sent by this function
1076// to understand which mutators are enabled
1077// also note that they aren't all registered mutators, e.g. jetpack, low gravity
1078void SendWelcomeMessage(entity this, int msg_type)
1079{
1080 if (boolean(autocvar_g_campaign))
1081 {
1082 WriteByte(msg_type, 1);
1083 WriteByte(msg_type, Campaign_GetLevelNum());
1084 return;
1085 }
1086
1087 int flags = 0;
1088 if (CS(this).version_mismatch)
1089 flags |= 2;
1090 if (CS(this).version < autocvar_gameversion)
1091 flags |= 4;
1093 if (MapInfo_Map_author != "")
1094 flags |= 8;
1095 WriteByte(msg_type, flags);
1096
1097 WriteString(msg_type, autocvar_hostname);
1099
1101 if (flags & 8)
1104
1106 WriteByte(msg_type, GetPlayerLimit());
1107
1108 MUTATOR_CALLHOOK(BuildMutatorsPrettyString, "");
1109 string modifications = M_ARGV(0, string);
1110
1111 if (g_weaponarena_type == WEPARENA_OFF && cvar("g_balance_blaster_weaponstartoverride") == 0)
1112 modifications = strcat(modifications, ", No start weapons");
1113 if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
1114 modifications = strcat(modifications, ", Low gravity");
1115 if(g_weapon_stay && !g_cts)
1116 modifications = strcat(modifications, ", Weapons stay");
1118 modifications = strcat(modifications, ", Jetpack");
1119 modifications = substring(modifications, 2, strlen(modifications) - 2);
1120
1121 WriteString(msg_type, modifications);
1122
1123 WriteByte(msg_type, g_weaponarena_type);
1126
1128 {
1131 }
1132
1133 WriteString(msg_type, cache_mutatormsg);
1134
1135 WriteString(msg_type, strreplace("\\n", "\n", autocvar_sv_motd));
1136}
1137
1147{
1148 if (Ban_MaybeEnforceBanOnce(this)) return;
1149 assert(!IS_CLIENT(this), return);
1150 this.flags |= FL_CLIENT;
1151 assert(player_count >= 0, player_count = 0);
1152
1153 TRANSMUTE(Client, this);
1154 CS(this).version_nagtime = time + 10 + random() * 10;
1155
1156 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname);
1157
1158 bot_clientconnect(this);
1159
1160 // after bot_clientconnect
1161 if (teamplay)
1162 this.clientcolors = 0;
1163
1164 this.team = -1;
1165
1166 TRANSMUTE(Observer, this);
1167
1168 PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid));
1169
1170 // always track bots, don't ask for cl_allow_uidtracking
1171 if (IS_BOT_CLIENT(this))
1173 else
1174 CS(this).allowed_timeouts = autocvar_sv_timeout_number;
1175
1177 GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? GameLog_ProcessIP(this.netaddress) : "bot"), ":", playername(this.netname, this.team, false)));
1178
1179 CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects
1180 this.wants_join = 0;
1181
1182 stuffcmd(this, clientstuff, "\n");
1183 stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
1184
1185 FixClientCvars(this);
1186
1187 // get version info from player
1188 stuffcmd(this, "cmd clientversion $gameversion\n");
1189
1190 // notify about available teams
1191 if (teamplay)
1192 {
1193 entity balance = TeamBalance_CheckAllowedTeams(this);
1194 int t = TeamBalance_GetAllowedTeams(balance);
1195 TeamBalance_Destroy(balance);
1196 stuffcmd(this, sprintf("set _teams_available %d\n", t));
1197 }
1198 else
1199 {
1200 stuffcmd(this, "set _teams_available 0\n");
1201 }
1202
1204
1205 CS(this).spectatortime = time;
1206 CS(this).jointime = time;
1207
1208 if (IS_REAL_CLIENT(this))
1209 {
1211
1212 if (g_weaponarena_weapons == WEPSET(TUBA))
1213 stuffcmd(this, "cl_cmd settemp chase_active 1\n");
1214 // quickmenu file must be put in a subfolder with an unique name
1215 // to reduce chances of overriding custom client quickmenus
1217 stuffcmd(this, sprintf("cl_cmd settemp _hud_panel_quickmenu_file_from_server %s\n", "wpeditor.txt"));
1219 stuffcmd(this, sprintf("cl_cmd settemp _hud_panel_quickmenu_file_from_server %s\n", autocvar_sv_quickmenu_file));
1220 }
1221
1222 CSQCMODEL_AUTOINIT(this);
1223
1224 CS(this).model_randomizer = random();
1225
1226 if (IS_REAL_CLIENT(this))
1227 sv_notice_join(this);
1228
1229 this.move_qcphysics = true;
1230
1231 // update physics stats (players can spawn before physics runs)
1232 Physics_UpdateStats(this);
1233
1234 IL_EACH(g_initforplayer, it.init_for_player,
1235 {
1236 it.init_for_player(it, this);
1237 });
1238
1239 Handicap_Initialize(this);
1240
1241 // playban
1243 TRANSMUTE(Observer, this);
1244
1245 if (PlayerInList(this, autocvar_g_chatban_list)) // chatban
1246 CS(this).muted = true;
1247
1249
1250 if (player_count == 1)
1251 {
1253 setpause(0);
1254 localcmd("\nsv_hook_firstjoin\n");
1255 }
1256
1257 if (get_nextmap() != "")
1259
1260 // cvar hook/callback TODO: recheck this when the cvar is changed
1263 this.vote_master = true;
1264}
1265
1268void player_powerups_remove_all(entity this, bool allow_poweroff_sound);
1269
1270/*
1271=============
1272ClientDisconnect
1273
1274Called when a client disconnects from the server
1275=============
1276*/
1278{
1279 assert(IS_CLIENT(this), return);
1280
1281 strfree(this.ignore_list);
1283 {
1284 // don't remove permanent ignores from db, only from list
1285 ignore_remove_player(it, this, false);
1286 });
1287
1289 if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
1290 if (CS(this).active_minigame) part_minigame(this);
1291 if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN, this.origin, '0 0 0', 1);
1292
1294 GameLogEcho(strcat(":part:", ftos(this.playerid)));
1295
1296 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
1297
1298 if(IS_SPEC(this))
1299 SetSpectatee(this, NULL);
1300
1302
1303 strfree(CS(this).netname_previous); // needs to be before the CS entity is removed!
1305 ClientState_detach(this);
1306
1307 Portal_ClearAll(this);
1308
1310
1312
1313 // Here, everything has been done that requires this player to be a client.
1314
1315 this.flags &= ~FL_CLIENT;
1316
1317 if (this.chatbubbleentity) delete(this.chatbubbleentity);
1318 if (this.killindicator) delete(this.killindicator);
1319
1320 IL_EACH(g_counters, it.realowner == this,
1321 {
1322 delete(it);
1323 });
1324
1326
1328
1329 strfree(this.clientstatus);
1330 if (this.personal) delete(this.personal);
1331
1332 this.playerid = 0;
1333 if (warmup_stage || game_starttime > time) /* warmup OR countdown */ ReadyCount();
1334 if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
1335
1336 player_powerups_remove_all(this, IS_PLAYER(this)); // stop powerup sound
1337
1338 ONREMOVE(this);
1339
1340 if (player_count == 0)
1341 localcmd("\nsv_hook_lastleave\n");
1342
1347}
1348
1350{
1351 this.nextthink = time;
1352
1353 entity ownr = this.owner;
1354 if (ownr.alpha < 0 || ownr.chatbubbleentity != this)
1355 {
1356 if (ownr) // but why can that ever be NULL?
1357 ownr.chatbubbleentity = NULL;
1358 delete(this);
1359 return;
1360 }
1361
1362 this.mdl = "";
1363
1364 if (!IS_DEAD(ownr) && IS_PLAYER(ownr) && !MUTATOR_CALLHOOK(ShowChatBubble, ownr, this))
1365 {
1367 this.mdl = "models/sprites/minigame_busy.iqm";
1368 else if (PHYS_INPUT_BUTTON_CHAT(ownr))
1369 this.mdl = "models/misc/chatbubble.spr";
1370 }
1371
1372 if (this.model != this.mdl)
1373 _setmodel(this, this.mdl);
1374}
1375
1377{
1378 if (this.alpha < 0)
1379 return;
1380 // spawn a chatbubble entity if needed
1381 if (!this.chatbubbleentity)
1382 {
1384 this.chatbubbleentity.owner = this;
1385 this.chatbubbleentity.exteriormodeltoclient = this;
1387 this.chatbubbleentity.nextthink = time;
1388 setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
1389 //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
1390 setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1');
1391 setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth
1392 this.chatbubbleentity.mdl = this.chatbubbleentity.model;
1393 //this.chatbubbleentity.model = "";
1394 this.chatbubbleentity.effects = EF_LOWPRECISION;
1395 }
1396}
1397
1399{
1400 if(MUTATOR_CALLHOOK(CalculateRespawnTime, this))
1401 return;
1402
1403 float gametype_setting_tmp;
1404 float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
1405 float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
1406 float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large);
1407 float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count);
1408 float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count);
1409 float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves);
1410
1411 float pcount = 1; // Include myself whether or not team is already set right and I'm a "player".
1412 if (teamplay)
1413 {
1414 FOREACH_CLIENT(IS_PLAYER(it) && it != this,
1415 {
1416 if(it.team == this.team)
1417 ++pcount;
1418 });
1419 if (sdelay_small_count == 0)
1420 sdelay_small_count = 1;
1421 if (sdelay_large_count == 0)
1422 sdelay_large_count = 1;
1423 }
1424 else
1425 {
1426 FOREACH_CLIENT(IS_PLAYER(it) && it != this,
1427 {
1428 ++pcount;
1429 });
1430 if (sdelay_small_count == 0)
1431 {
1432 if (IS_INDEPENDENT_PLAYER(this))
1433 {
1434 // Players play independently. No point in requiring enemies.
1435 sdelay_small_count = 1;
1436 }
1437 else
1438 {
1439 // Players play AGAINST each other. Enemies required.
1440 sdelay_small_count = 2;
1441 }
1442 }
1443 if (sdelay_large_count == 0)
1444 {
1445 if (IS_INDEPENDENT_PLAYER(this))
1446 {
1447 // Players play independently. No point in requiring enemies.
1448 sdelay_large_count = 1;
1449 }
1450 else
1451 {
1452 // Players play AGAINST each other. Enemies required.
1453 sdelay_large_count = 2;
1454 }
1455 }
1456 }
1457
1458 float sdelay;
1459
1460 if (pcount <= sdelay_small_count)
1461 sdelay = sdelay_small;
1462 else if (pcount >= sdelay_large_count)
1463 sdelay = sdelay_large;
1464 else // NOTE: this case implies sdelay_large_count > sdelay_small_count.
1465 sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count);
1466
1467 if(waves)
1468 this.respawn_time = ceil((time + sdelay) / waves) * waves;
1469 else
1470 this.respawn_time = time + sdelay;
1471
1472 if(sdelay < sdelay_max)
1473 this.respawn_time_max = time + sdelay_max;
1474 else
1475 this.respawn_time_max = this.respawn_time;
1476
1477 if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75))
1478 this.respawn_countdown = 10; // first number to count down from is 10
1479 else
1480 this.respawn_countdown = -1; // do not count down
1481
1484}
1485
1486// LordHavoc: this hack will be removed when proper _pants/_shirt layers are
1487// added to the model skins
1488/*void UpdateColorModHack()
1489{
1490 float c = this.clientcolors & 15;
1491 // LordHavoc: only bothering to support white, green, red, yellow, blue
1492 if (!teamplay) this.colormod = '0 0 0';
1493 else if (c == 0) this.colormod = '1.00 1.00 1.00';
1494 else if (c == 3) this.colormod = '0.10 1.73 0.10';
1495 else if (c == 4) this.colormod = '1.73 0.10 0.10';
1496 else if (c == 12) this.colormod = '1.22 1.22 0.10';
1497 else if (c == 13) this.colormod = '0.10 0.10 1.73';
1498 else this.colormod = '1 1 1';
1499}*/
1500
1501void respawn(entity this)
1502{
1503 bool damagedbycontents_prev = this.damagedbycontents;
1504 if(this.alpha >= 0)
1505 {
1507 {
1508 this.solid = SOLID_NOT;
1509 this.takedamage = DAMAGE_NO;
1510 this.damagedbycontents = false;
1516 Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1);
1519 }
1520 else
1521 SUB_SetFade (this, time, 1); // fade out the corpse immediately
1522 }
1523
1524 CopyBody(this, 1);
1525 this.damagedbycontents = damagedbycontents_prev;
1526
1527 this.effects |= EF_NODRAW; // prevent another CopyBody
1528 PutClientInServer(this);
1529}
1530
1531void play_countdown(entity this, float finished, Sound samp)
1532{
1533 TC(Sound, samp);
1534 float time_left = finished - time;
1535 if(IS_REAL_CLIENT(this) && time_left < 6 && floor(time_left - frametime) != floor(time_left))
1536 sound(this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
1537}
1538
1539// it removes special powerups not handled by StatusEffects
1540void player_powerups_remove_all(entity this, bool allow_poweroff_sound)
1541{
1543 {
1544 // don't play the poweroff sound when the game restarts or the player disconnects
1545 if (allow_poweroff_sound && time > game_starttime + 1 && IS_CLIENT(this)
1547 sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
1548
1550 stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
1551
1553 }
1554}
1555
1557{
1558 if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !game_stopped)
1559 this.modelflags |= MF_ROCKET;
1560 else
1561 this.modelflags &= ~MF_ROCKET;
1562
1563 this.effects &= ~EF_NODEPTHTEST;
1564
1565 if (IS_DEAD(this))
1566 player_powerups_remove_all(this, true);
1567
1568 if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
1569 return;
1570
1571 // add a way to see what the items were BEFORE all of these checks for the mutator hook
1572 int items_prev = this.items;
1573
1574 if (!MUTATOR_IS_ENABLED(mutator_instagib))
1575 {
1576 // NOTE: superweapons are a special case and as such are handled here instead of the status effects system
1577 if (this.items & IT_SUPERWEAPON)
1578 {
1579 if (!(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS))
1580 {
1581 StatusEffects_remove(STATUSEFFECT_Superweapon, this, STATUSEFFECT_REMOVE_NORMAL);
1582 this.items &= ~IT_SUPERWEAPON;
1583 //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
1584 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
1585 }
1586 else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
1587 {
1588 // don't let them run out
1589 }
1590 else
1591 {
1592 play_countdown(this, StatusEffects_gettime(STATUSEFFECT_Superweapon, this), SND_POWEROFF);
1593 if (time >= StatusEffects_gettime(STATUSEFFECT_Superweapon, this))
1594 {
1595 this.items &= ~IT_SUPERWEAPON;
1596 STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
1597 //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
1598 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
1599 }
1600 }
1601 }
1602 else if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
1603 {
1604 if (time < StatusEffects_gettime(STATUSEFFECT_Superweapon, this) || (this.items & IT_UNLIMITED_SUPERWEAPONS))
1605 {
1606 this.items |= IT_SUPERWEAPON;
1607 if(!(this.items & IT_UNLIMITED_SUPERWEAPONS))
1608 {
1609 if(!g_cts)
1610 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
1611 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
1612 }
1613 }
1614 else
1615 {
1616 if(StatusEffects_active(STATUSEFFECT_Superweapon, this))
1617 StatusEffects_remove(STATUSEFFECT_Superweapon, this, STATUSEFFECT_REMOVE_TIMEOUT);
1618 STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
1619 }
1620 }
1621 else if(StatusEffects_active(STATUSEFFECT_Superweapon, this)) // cheaper to check than to update each frame!
1622 {
1623 StatusEffects_remove(STATUSEFFECT_Superweapon, this, STATUSEFFECT_REMOVE_CLEAR);
1624 }
1625 }
1626
1628 this.effects |= EF_NODEPTHTEST;
1629
1631 this.effects |= EF_FULLBRIGHT;
1632
1633 MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
1634}
1635
1636float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
1637{
1638 if(current > stable)
1639 return current;
1640 else if(current > stable - 0.25) // when close enough, "snap"
1641 return stable;
1642 else
1643 return min(stable, current + (stable - current) * regenfactor * regenframetime);
1644}
1645
1646float CalcRot(float current, float stable, float rotfactor, float rotframetime)
1647{
1648 if(current < stable)
1649 return current;
1650 else if(current < stable + 0.25) // when close enough, "snap"
1651 return stable;
1652 else
1653 return max(stable, current + (stable - current) * rotfactor * rotframetime);
1654}
1655
1656void RotRegen(entity this, Resource res, float limit_mod,
1657 float regenstable, float regenfactor, float regenlinear, float regenframetime,
1658 float rotstable, float rotfactor, float rotlinear, float rotframetime)
1659{
1660 float old = GetResource(this, res);
1661 float current = old;
1662 if(current > rotstable)
1663 {
1664 if(rotframetime > 0)
1665 {
1666 current = CalcRot(current, rotstable, rotfactor, rotframetime);
1667 current = max(rotstable, current - rotlinear * rotframetime);
1668 }
1669 }
1670 else if(current < regenstable)
1671 {
1672 if(regenframetime > 0)
1673 {
1674 current = CalcRegen(current, regenstable, regenfactor, regenframetime);
1675 current = min(regenstable, current + regenlinear * regenframetime);
1676 }
1677 }
1678
1679 float limit = GetResourceLimit(this, res) * limit_mod;
1680 if(current > limit)
1681 current = limit;
1682
1683 if (current != old)
1684 SetResource(this, res, current);
1685}
1686
1688{
1689 float max_mod, regen_mod, rot_mod, limit_mod;
1690 max_mod = regen_mod = rot_mod = limit_mod = 1;
1691
1692 float regen_health = autocvar_g_balance_health_regen;
1693 float regen_health_linear = autocvar_g_balance_health_regenlinear;
1694 float regen_health_rot = autocvar_g_balance_health_rot;
1695 float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
1696 float regen_health_stable = autocvar_g_balance_health_regenstable;
1697 float regen_health_rotstable = autocvar_g_balance_health_rotstable;
1698 bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
1699 regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
1700 max_mod = M_ARGV(1, float);
1701 regen_mod = M_ARGV(2, float);
1702 rot_mod = M_ARGV(3, float);
1703 limit_mod = M_ARGV(4, float);
1704 regen_health = M_ARGV(5, float);
1705 regen_health_linear = M_ARGV(6, float);
1706 regen_health_rot = M_ARGV(7, float);
1707 regen_health_rotlinear = M_ARGV(8, float);
1708 regen_health_stable = M_ARGV(9, float);
1709 regen_health_rotstable = M_ARGV(10, float);
1710
1711 float rotstable, regenstable, rotframetime, regenframetime;
1712
1713 if(!mutator_returnvalue)
1714 {
1717 regenframetime = (time > this.pauseregen_finished) ? (regen_mod * frametime) : 0;
1718 rotframetime = (time > this.pauserotarmor_finished) ? (rot_mod * frametime) : 0;
1719 RotRegen(this, RES_ARMOR, limit_mod,
1722
1723 // NOTE: max_mod is only applied to health
1724 regenstable = regen_health_stable * max_mod;
1725 rotstable = regen_health_rotstable * max_mod;
1726 regenframetime = (time > this.pauseregen_finished) ? (regen_mod * frametime) : 0;
1727 rotframetime = (time > this.pauserothealth_finished) ? (rot_mod * frametime) : 0;
1728 RotRegen(this, RES_HEALTH, limit_mod,
1729 regenstable, regen_health, regen_health_linear, regenframetime,
1730 rotstable, regen_health_rot, regen_health_rotlinear, rotframetime);
1731 }
1732
1733 // if player rotted to death... die!
1734 // check this outside above checks, as player may still be able to rot to death
1735 if(GetResource(this, RES_HEALTH) < 1)
1736 {
1737 if(this.vehicle)
1739 if(this.event_damage)
1740 this.event_damage(this, this, this, 1, DEATH_ROT.m_id, DMG_NOWEP, this.origin, '0 0 0');
1741 }
1742
1743 if (!(this.items & IT_UNLIMITED_AMMO))
1744 {
1747 regenframetime = ((time > this.pauseregen_finished) && (this.items & ITEM_FuelRegen.m_itemid)) ? frametime : 0;
1748 rotframetime = (time > this.pauserotfuel_finished) ? frametime : 0;
1749 RotRegen(this, RES_FUEL, 1,
1752 }
1753}
1754
1756void SetZoomState(entity this, float newzoom)
1757{
1758 if(newzoom != CS(this).zoomstate)
1759 {
1760 CS(this).zoomstate = newzoom;
1761 ClientData_Touch(this, true);
1762 }
1763 zoomstate_set = true;
1764}
1765
1767{
1769 if (game_stopped)
1770 {
1771 CS(this).pressedkeys = 0;
1772 STAT(PRESSED_KEYS, this) = 0;
1773 return;
1774 }
1775
1776 // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
1777 int keys = STAT(PRESSED_KEYS, this);
1778 keys = BITSET(keys, KEY_FORWARD, CS(this).movement.x > 0);
1779 keys = BITSET(keys, KEY_BACKWARD, CS(this).movement.x < 0);
1780 keys = BITSET(keys, KEY_RIGHT, CS(this).movement.y > 0);
1781 keys = BITSET(keys, KEY_LEFT, CS(this).movement.y < 0);
1782
1783 keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this));
1784 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
1785 keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
1786 keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
1787 CS(this).pressedkeys = keys; // store for other users
1788
1789 STAT(PRESSED_KEYS, this) = keys;
1790}
1791
1792/*
1793======================
1794spectate mode routines
1795======================
1796*/
1797
1798void SpectateCopy(entity this, entity spectatee)
1799{
1800 TC(Client, this); TC(Client, spectatee);
1801
1802 MUTATOR_CALLHOOK(SpectateCopy, spectatee, this);
1803 PS(this) = PS(spectatee);
1804 this.armortype = spectatee.armortype;
1806 SetResourceExplicit(this, RES_CELLS, GetResource(spectatee, RES_CELLS));
1807 SetResourceExplicit(this, RES_SHELLS, GetResource(spectatee, RES_SHELLS));
1808 SetResourceExplicit(this, RES_BULLETS, GetResource(spectatee, RES_BULLETS));
1809 SetResourceExplicit(this, RES_ROCKETS, GetResource(spectatee, RES_ROCKETS));
1810 SetResourceExplicit(this, RES_FUEL, GetResource(spectatee, RES_FUEL));
1811 this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
1812 SetResourceExplicit(this, RES_HEALTH, GetResource(spectatee, RES_HEALTH));
1813 CS(this).impulse = 0;
1814 this.disableclientprediction = 1; // no need to run prediction on a spectator
1815 this.items = spectatee.items;
1816 STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee);
1817 STAT(HIT_TIME, this) = STAT(HIT_TIME, spectatee);
1818 STAT(AIR_FINISHED, this) = STAT(AIR_FINISHED, spectatee);
1819 STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
1820 STAT(WEAPONS, this) = STAT(WEAPONS, spectatee);
1821 this.punchangle = spectatee.punchangle;
1822 this.view_ofs = spectatee.view_ofs;
1823 this.velocity = spectatee.velocity;
1824 this.dmg_take = spectatee.dmg_take;
1825 this.dmg_save = spectatee.dmg_save;
1826 this.dmg_inflictor = spectatee.dmg_inflictor;
1827 this.v_angle = spectatee.v_angle;
1828 this.angles = spectatee.v_angle;
1829 this.viewloc = spectatee.viewloc;
1830 if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
1831 this.fixangle = true;
1832 setorigin(this, spectatee.origin);
1833 setsize(this, spectatee.mins, spectatee.maxs);
1834 SetZoomState(this, CS(spectatee).zoomstate);
1835
1836 anticheat_spectatecopy(this, spectatee);
1837 STAT(HUD, this) = STAT(HUD, spectatee);
1838 if(spectatee.vehicle)
1839 {
1840 this.angles = spectatee.v_angle;
1841
1842 //this.fixangle = false;
1843 //this.velocity = spectatee.vehicle.velocity;
1844 this.vehicle_health = spectatee.vehicle_health;
1845 this.vehicle_shield = spectatee.vehicle_shield;
1846 this.vehicle_energy = spectatee.vehicle_energy;
1847 this.vehicle_ammo1 = spectatee.vehicle_ammo1;
1848 this.vehicle_ammo2 = spectatee.vehicle_ammo2;
1849 this.vehicle_reload1 = spectatee.vehicle_reload1;
1850 this.vehicle_reload2 = spectatee.vehicle_reload2;
1851
1852 //msg_entity = this;
1853
1854 // WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
1855 //WriteAngle(MSG_ONE, spectatee.v_angle.x);
1856 // WriteAngle(MSG_ONE, spectatee.v_angle.y);
1857 // WriteAngle(MSG_ONE, spectatee.v_angle.z);
1858
1859 //WriteByte (MSG_ONE, SVC_SETVIEW);
1860 // WriteEntity(MSG_ONE, this);
1861 //makevectors(spectatee.v_angle);
1862 //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/
1863 }
1864}
1865
1867{
1868 if(!this.enemy)
1869 return false;
1870
1871 if(!IS_PLAYER(this.enemy) || this == this.enemy)
1872 {
1873 SetSpectatee(this, NULL);
1874 return false;
1875 }
1876
1877 SpectateCopy(this, this.enemy);
1878 if (IS_OBSERVER(this))
1879 TRANSMUTE(Spectator, this);
1880
1881 return true;
1882}
1883
1885{
1886 if(!IS_PLAYER(this.enemy))
1887 return false;
1888
1889 ClientData_Touch(this.enemy, true);
1890
1891 msg_entity = this;
1893 WriteEntity(MSG_ONE, this.enemy);
1895 accuracy_resend(this);
1896
1897 if(!SpectateUpdate(this))
1898 PutObserverInServer(this, false, true);
1899
1900 return true;
1901}
1902
1903void SetSpectatee_status(entity this, int spectatee_num)
1904{
1905 int oldspectatee_status = CS(this).spectatee_status;
1906 CS(this).spectatee_status = spectatee_num;
1907
1908 if (CS(this).spectatee_status != oldspectatee_status)
1909 {
1910 if (STAT(PRESSED_KEYS, this))
1911 {
1912 CS(this).pressedkeys = 0;
1913 STAT(PRESSED_KEYS, this) = 0;
1914 }
1915
1916 ClientData_Touch(this, true);
1917
1918 // init or clear race data
1919 if ((g_race || g_cts) && g_race_qualifying && IS_REAL_CLIENT(this))
1920 {
1921 msg_entity = this;
1922
1923 if (this.enemy && this.enemy.race_laptime)
1924 {
1925 // init
1927 }
1928 else
1929 {
1930 // send reset to this spectator
1931 WriteHeader(MSG_ONE, TE_CSQC_RACE);
1933 }
1934 }
1935 }
1936}
1937
1938void SetSpectatee(entity this, entity spectatee)
1939{
1940 if(IS_BOT_CLIENT(this))
1941 return; // bots abuse .enemy, this code is useless to them
1942
1943 entity old_spectatee = this.enemy;
1944
1945 this.enemy = spectatee;
1946
1947 // WEAPONTODO
1948 // these are required to fix the spectator bug with arc
1949 if(old_spectatee)
1950 {
1951 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
1952 {
1953 .entity weaponentity = weaponentities[slot];
1954 if(old_spectatee.(weaponentity).arc_beam)
1955 old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_UPDATE;
1956 }
1957 }
1958 if(spectatee)
1959 {
1960 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
1961 {
1962 .entity weaponentity = weaponentities[slot];
1963 if(spectatee.(weaponentity).arc_beam)
1964 spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_UPDATE;
1965 }
1966 }
1967
1968 if (spectatee)
1969 SetSpectatee_status(this, etof(spectatee));
1970
1971 // needed to update spectator list
1972 if(old_spectatee)
1973 ClientData_Touch(old_spectatee, true);
1974}
1975
1976bool Spectate(entity this, entity pl)
1977{
1978 if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
1979 return false;
1980 pl = M_ARGV(1, entity);
1981
1982 SetSpectatee(this, pl);
1983 return SpectateSet(this);
1984}
1985
1987{
1988 entity ent = find(this.enemy, classname, STR_PLAYER);
1989
1990 if (MUTATOR_CALLHOOK(SpectateNext, this, ent))
1991 ent = M_ARGV(1, entity);
1992 else if (!ent)
1993 ent = find(ent, classname, STR_PLAYER);
1994
1995 if(ent) SetSpectatee(this, ent);
1996
1997 return SpectateSet(this);
1998}
1999
2001{
2002 // NOTE: chain order is from the highest to the lower entnum (unlike find)
2003 entity ent = findchain(classname, STR_PLAYER);
2004 if (!ent) // no player
2005 return false;
2006
2007 entity first = ent;
2008 // skip players until current spectated player
2009 if(this.enemy)
2010 while(ent && ent != this.enemy)
2011 ent = ent.chain;
2012
2013 switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first))
2014 {
2015 case MUT_SPECPREV_FOUND:
2016 ent = M_ARGV(1, entity);
2017 break;
2019 return true;
2021 default:
2022 {
2023 if(ent.chain)
2024 ent = ent.chain;
2025 else
2026 ent = first;
2027 break;
2028 }
2029 }
2030
2031 SetSpectatee(this, ent);
2032 return SpectateSet(this);
2033}
2034
2035/*
2036=============
2037ShowRespawnCountdown()
2038
2039Update a respawn countdown display.
2040=============
2041*/
2043{
2044 float number;
2045 if(!IS_DEAD(this)) // just respawned?
2046 return;
2047 else
2048 {
2049 number = ceil(this.respawn_time - time);
2050 if(number <= 0)
2051 return;
2052 if(number <= this.respawn_countdown)
2053 {
2054 this.respawn_countdown = number - 1;
2055 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
2056 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number));
2057 }
2058 }
2059}
2060
2062{
2064 return false;
2065 if (QueuedPlayersReady(this, true))
2066 return false;
2067 if (frametime) // once per frame is more than enough
2068 stuffcmd(this, "_scoreboard_team_selection 1\n");
2069 return true;
2070}
2071
2073void Join(entity this, bool queued_join)
2074{
2075 entity player_with_dibs = NULL;
2076
2078 {
2080 ReadyRestart(true);
2082 }
2083
2084 // If teams aren't equal and a player is queued then they chose a biggest team so they can't join yet.
2085 if (queued_join && TeamBalance_AreEqual(this, true))
2086 {
2087 int clients_remaining = AVAILABLE_TEAMS;
2088 // First we must join player(s) queued for specific team(s) (they chose first)
2089 // so TeamBalance_JoinBestTeam() won't select the same team(s).
2090 // Assumes `this` was already assigned a team or is using autoselect.
2091 FOREACH_CLIENT(it != this && it.wants_join > 0,
2092 {
2093 // detect any conflict between `this` and a queued player (queuePlayer() handles other conflicts)
2094 if (this.team < 0 && this.team_selected > 0 // `this` can't have their preference
2095 && it.wants_join == this.team_selected) // `it` is the player who already chose the team `this` wanted
2096 player_with_dibs = it;
2097
2098 Join(it, false);
2099 --clients_remaining;
2100 });
2101
2102 // Second pass: queued players whose team will be autoselected
2103 // in order of strongest to weakest for optimal balance.
2104 for (; clients_remaining; --clients_remaining)
2105 {
2106 entity strongest_autoselect_client = NULL;
2107 float strongest_autoselect_client_skill = 0;
2108 FOREACH_CLIENT(it.wants_join < 0 || (it == this && it.team <= 0),
2109 {
2110 float it_skill = it.m_skill_mu ? it.m_skill_mu : server_skill_average; // average was set by TeamBalance_AreEqual()
2111 if (it_skill > strongest_autoselect_client_skill)
2112 {
2113 strongest_autoselect_client = it;
2114 strongest_autoselect_client_skill = it_skill;
2115 }
2116 });
2117 if (strongest_autoselect_client == this) // can't just Join(); return; due to player_with_dibs
2118 {
2119 entity balance = TeamBalance_CheckAllowedTeams(this);
2120 int best_team_index = TeamBalance_FindBestTeam(balance, this, true);
2121 TeamBalance_Destroy(balance);
2122 SetPlayerTeam(this, best_team_index, TEAM_CHANGE_AUTO);
2123 }
2124 else if (strongest_autoselect_client)
2125 Join(strongest_autoselect_client, false);
2126 }
2127 }
2128
2129 Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
2130
2131 TRANSMUTE(Player, this);
2132 PutClientInServer(this);
2133
2134 if(IS_PLAYER(this)) // could be false due to PutClientInServer() mutator hook
2135 {
2136 if (!teamplay)
2137 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
2138 else if (player_with_dibs)
2139 // limitation: notifications support only 1 translated team name
2140 // so the team `this` preferred can't be mentioned, only the team they got assigned to.
2141 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PLAY_TEAM_QUEUECONFLICT), player_with_dibs.netname);
2142 else
2143 {
2144 if (this.wants_join && game_starttime < time && !warmup_stage) // No countdown running && not returning to warmup via ReadyRestart_force
2145 Send_Notification(NOTIF_ONE_ONLY, this, MSG_ANNCE, ANNCE_BEGIN); // Get queued player's attention
2146 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_JOIN_PLAY_TEAM));
2147 }
2148 }
2149
2150 this.team_selected = 0;
2151 this.wants_join = 0;
2152}
2153
2155{
2156 if(g_duel)
2157 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)
2158
2159 // don't return map_maxplayers during intermission as it would interfere with MapHasRightSize()
2160 int player_limit = autocvar_g_maxplayers > 0 ? autocvar_g_maxplayers : maxclients;
2162 {
2163 if (autocvar_g_maxplayers == -1 && map_maxplayers > 0)
2164 player_limit = map_maxplayers;
2165 MUTATOR_CALLHOOK(GetPlayerLimit, player_limit);
2166 player_limit = M_ARGV(0, int);
2167 }
2168 return bound(0, player_limit, maxclients);
2169}
2170
2177{
2178 if(!this)
2179 // this is called that way when checking if anyone may be able to join (to build qcstatus)
2180 // so report 0 free slots if restricted
2181 {
2182 if(autocvar_g_forced_team_otherwise == "spectate"
2183 || autocvar_g_forced_team_otherwise == "spectator"
2184 || (teamplay && lockteams))
2185 return 0;
2186 }
2187
2188 int totalClients = 0;
2189 int currentlyPlaying = 0;
2190 FOREACH_CLIENT(it != this,
2191 {
2192 ++totalClients;
2193 if(IS_PLAYER(it) || INGAME(it))
2194 ++currentlyPlaying;
2195 });
2196
2197 int player_limit = GetPlayerLimit();
2198
2199 int free_slots = max(0, player_limit - currentlyPlaying);
2200 if (this || maxclients - totalClients) // don't add bot slots in the getstatus case if nobody can connect
2201 free_slots += bots_would_leave;
2202 if (!this) // getstatus case
2203 free_slots = min(free_slots, maxclients - totalClients);
2204
2205 if(this && !free_slots)
2206 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_MAXPLAYERS, player_limit);
2207
2208 return free_slots;
2209}
2210
2211// Callsites other than ClientCommand_join() teamplay should pass this.wants_join as team_index
2212// so the player won't accidentally reset a specific preference by pressing +jump
2213// and will see the centreprint with their current preference each time they press +jump.
2214bool queuePlayer(entity this, int team_index)
2215{
2216 // check if a queued player already chose the selected team
2217 if (team_index > 0)
2218 {
2219 FOREACH_CLIENT(it != this && it.wants_join == team_index,
2220 {
2221 if (QueuedPlayersReady(this, false))
2222 {
2223 // Join() will handle the notification so it can mention the team `player` will actually get
2224 this.team = -1; // force autoselect in Join() (last player skips queue)
2225 this.team_selected = team_index; // tell it which team to check for to find the conflict
2226 }
2227 else // > 2 teams
2228 {
2229 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(Team_IndexToTeam(team_index), CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT), it.netname);
2230 this.wants_join = -1; // force autoselect in Join()
2231 this.team_selected = -1; // prevents clobbering by CENTER_JOIN_PREVENT_QUEUE
2232 }
2233 return true;
2234 });
2235 }
2236
2237 if (QueuedPlayersReady(this, false))
2238 return false;
2239
2240 if (team_index <= 0) // team auto select deferred until Join()
2241 {
2242 if (team_index != this.wants_join || !this.wants_join) // prevents chatcon spam
2243 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_WANTS, this.netname);
2244 if (this.team_selected >= 0) // prevents CENTER_JOIN_PREVENT_QUEUE_TEAM_CONFLICT getting clobbered
2245 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_QUEUE);
2246 this.wants_join = -1;
2247 this.team_selected = 0;
2248 }
2249 else
2250 {
2251 int team_num = Team_IndexToTeam(team_index);
2252 if (team_index != this.wants_join) // prevents chatcon spam
2253 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(team_num, INFO_JOIN_WANTS_TEAM), this.netname);
2254 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(team_num, CENTER_JOIN_PREVENT_QUEUE_TEAM));
2255 this.wants_join = team_index; // Player queued to join
2256 this.team_selected = team_index;
2257 }
2258
2259 return true;
2260}
2261
2263bool joinAllowed(entity this, int team_index)
2264{
2265 if (CS(this).version_mismatch)
2266 {
2267 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_VERSIONMISMATCH);
2268 return false;
2269 }
2270 if (time < CS(this).jointime + MIN_SPEC_TIME) return false;
2271 if (teamplay && lockteams)
2272 {
2273 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_LOCKED);
2274 return false;
2275 }
2276
2277 int forced_team_index = Player_GetForcedTeamIndex(this);
2278 if (forced_team_index < TEAM_FORCE_DEFAULT || (teamplay && forced_team_index > AVAILABLE_TEAMS)) // PutPlayerInServer would bug out
2279 {
2280 if (forced_team_index > AVAILABLE_TEAMS)
2281 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(Team_IndexToTeam(forced_team_index), CENTER_TEAMCHANGE_FORCEDTEAM));
2282 else // handles !teamplay case too
2283 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_FORCEDSPEC);
2284 return false;
2285 }
2286
2287 if (!INGAME(this) && PlayerInList(this, autocvar_g_playban_list))
2288 {
2289 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PLAYBAN);
2290 return false;
2291 }
2292
2293 // IS_PLAYER for most gametypes, INGAME for ca, lms, surv
2294 if (autocvar_g_maxping && !(IS_PLAYER(this) || INGAME(this)) && !this.wants_join)
2295 {
2296 if (this.ping <= 0) return false; // too soon
2297 if (this.ping > autocvar_g_maxping)
2298 {
2299 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT_PING);
2300 return false;
2301 }
2302 }
2303
2304 if (QueueNeeded(this, team_index))
2305 {
2306 if (team_index == 0) // so ClientCommand_join() can check joinAllowed() before calling SetPlayerTeam() without chicken/egg problem
2307 if (ShowTeamSelection(this)) return false; // only needed by callsites other than ClientCommand_join() teamplay
2308 // queuePlayer called here so that only conditions above block queuing (g_maxplayers shouldn't)
2309 if (queuePlayer(this, team_index)) return false;
2310 if (!nJoinAllowed(this)) return false;
2311 }
2312 else
2313 {
2314 if (!nJoinAllowed(this)) return false;
2315 if (team_index == 0) // so ClientCommand_join() can check joinAllowed() before calling SetPlayerTeam() without chicken/egg problem
2316 if (ShowTeamSelection(this)) return false; // only needed by callsites other than ClientCommand_join() teamplay
2317 }
2318
2319 return true;
2320}
2321
2323{
2324 // waypoint editor implements a similar feature for waypoints
2326 return;
2327
2328 if (wasfreed(this.wp_aimed))
2329 this.wp_aimed = NULL;
2330
2332 entity ent = NULL;
2333 if (trace_ent)
2334 {
2335 ent = trace_ent;
2336 if (ent != this.wp_aimed)
2337 {
2338 string str = sprintf(
2339 "^7ent #%d\n^8 netname: ^3%s\n^8 classname: ^5%s\n^8 origin: ^2'%s'",
2340 etof(ent), ent.netname, ent.classname, vtos(ent.origin));
2341 debug_text_3d((ent.absmin + ent.absmax) * 0.5, str, 0, 7, '0 0 0');
2342 }
2343 }
2344 if (this.wp_aimed != ent)
2345 this.wp_aimed = ent;
2346}
2347
2350{
2352 {
2353 this.modelflags &= ~MF_ROCKET;
2355 IntermissionThink(this);
2356 return false;
2357 }
2358
2360 {
2361 // don't allow the player to turn around while game is paused
2362 // FIXME turn this into CSQC stuff
2363 this.v_angle = this.lastV_angle;
2364 this.angles = this.lastV_angle;
2365 this.fixangle = true;
2366 }
2367
2368 if (frametime) player_powerups(this);
2369
2371
2372 if (IS_DEAD(this))
2373 {
2374 if (this.personal && g_race_qualifying
2375 && (autocvar_g_allow_checkpoints || CheatsAllowed(this, CHIMPULSE_SPEEDRUN.impulse, 0, 0, false, true)))
2376 {
2377 if (time > this.respawn_time)
2378 {
2379 STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
2380 respawn(this);
2381 CS(this).impulse = CHIMPULSE_SPEEDRUN.impulse;
2382 }
2383 }
2384 else
2385 {
2386 if (frametime) player_anim(this);
2387
2388 if (this.respawn_flags & RESPAWN_DENY)
2389 {
2390 STAT(RESPAWN_TIME, this) = 0;
2391 return false;
2392 }
2393
2394 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));
2395
2396 switch(this.deadflag)
2397 {
2398 case DEAD_DYING:
2399 {
2400 if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
2402 else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
2403 this.deadflag = DEAD_DEAD;
2404 break;
2405 }
2406 case DEAD_DEAD:
2407 {
2408 if (button_pressed)
2410 else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
2412 break;
2413 }
2414 case DEAD_RESPAWNABLE:
2415 {
2416 if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
2418 break;
2419 }
2420 case DEAD_RESPAWNING:
2421 {
2422 if (time > this.respawn_time)
2423 {
2424 this.respawn_time = time + 1; // only retry once a second
2425 this.respawn_time_max = this.respawn_time;
2426 respawn(this);
2427 }
2428 break;
2429 }
2430 }
2431
2433
2434 if (this.respawn_flags & RESPAWN_SILENT)
2435 STAT(RESPAWN_TIME, this) = 0;
2436 else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
2437 {
2438 if (time < this.respawn_time)
2439 STAT(RESPAWN_TIME, this) = this.respawn_time;
2440 else if (this.deadflag != DEAD_RESPAWNING)
2441 STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
2442 }
2443 else
2444 STAT(RESPAWN_TIME, this) = this.respawn_time;
2445 }
2446
2447 // if respawning, invert stat_respawn_time to indicate this, the client translates it
2448 if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
2449 STAT(RESPAWN_TIME, this) *= -1;
2450
2451 return false;
2452 }
2453
2454 FixPlayermodel(this);
2455
2457 {
2459 stuffcmd(this, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin));
2460 }
2461
2462 // reset gun alignment when dual wielding status changes
2463 // to ensure guns are always aligned right and left
2464 bool dualwielding = W_DualWielding(this);
2465 if(this.dualwielding_prev != dualwielding)
2466 {
2467 W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
2468 this.dualwielding_prev = dualwielding;
2469 }
2470
2471 if (frametime)
2472 {
2473 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2474 {
2475 .entity weaponentity = weaponentities[slot];
2476 if(WEP_CVAR(WEP_VORTEX, charge_always))
2477 W_Vortex_Charge(this, weaponentity, frametime);
2478 W_WeaponFrame(this, weaponentity);
2479 }
2480
2481 // WEAPONTODO: Add a weapon request for this
2482 // rot vortex charge to the charge limit
2483 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2484 {
2485 .entity weaponentity = weaponentities[slot];
2486 float chargelimit = WEP_CVAR(WEP_VORTEX, charge_limit);
2487 float chargerate = WEP_CVAR(WEP_VORTEX, charge_rot_rate);
2488 if(WEP_CVAR(WEP_VORTEX, charge_rot_unequipped_rate) && this.(weaponentity).m_weapon != WEP_VORTEX)
2489 {
2490 chargelimit = WEP_CVAR(WEP_VORTEX, charge_rot_unequipped_limit);
2491 chargerate = WEP_CVAR(WEP_VORTEX, charge_rot_unequipped_rate);
2492 }
2493
2494 if (chargerate && this.(weaponentity).vortex_charge > chargelimit && this.(weaponentity).vortex_charge_rottime < time)
2495 this.(weaponentity).vortex_charge = bound(chargelimit, this.(weaponentity).vortex_charge - chargerate * frametime / W_TICSPERFRAME, 1);
2496 }
2497
2498 player_regen(this);
2499 player_anim(this);
2501 }
2502
2503 monsters_setstatus(this);
2504
2505 return true;
2506}
2507
2509// merged SpectatorThink and ObserverThink (old names are here so you can grep for them)
2511{
2512 bool is_spec = IS_SPEC(this);
2513 if ( CS(this).impulse )
2514 {
2515 int r = MinigameImpulse(this, CS(this).impulse);
2516 if (!is_spec || r)
2517 CS(this).impulse = 0;
2518
2519 if (is_spec && CS(this).impulse == IMP_weapon_drop.impulse)
2520 {
2521 STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3;
2522 CS(this).impulse = 0;
2523 return;
2524 }
2525 }
2526
2528
2529 if (IS_BOT_CLIENT(this) && !CS(this).autojoin_checked)
2530 {
2531 CS(this).autojoin_checked = 1;
2532 TRANSMUTE(Player, this);
2533 PutClientInServer(this);
2534
2535 .entity weaponentity = weaponentities[0];
2536 if(this.(weaponentity).m_weapon == WEP_Null)
2537 W_NextWeapon(this, 0, weaponentity);
2538
2539 return;
2540 }
2541
2542 if (this.flags & FL_JUMPRELEASED)
2543 {
2544 if (PHYS_INPUT_BUTTON_JUMP(this) && (joinAllowed(this, this.wants_join) || time < CS(this).jointime + MIN_SPEC_TIME))
2545 {
2546 this.flags &= ~FL_JUMPRELEASED;
2547 this.flags |= FL_SPAWNING;
2548 }
2549 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)))
2550 || (!is_spec && ((PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch) || this.would_spectate)))
2551 {
2552 this.flags &= ~FL_JUMPRELEASED;
2553 if (autocvar_sv_spectate == 2 && !warmup_stage && !this.vote_master)
2554 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_SPECTATE_SPEC_NOTALLOWED);
2555 else if(SpectateNext(this))
2556 { }
2557 else if (is_spec)
2558 {
2559 TRANSMUTE(Observer, this);
2560 PutClientInServer(this);
2561 }
2562 else
2563 this.would_spectate = false; // unable to spectate anyone
2564
2565 if (is_spec)
2566 CS(this).impulse = 0;
2567 }
2568 else if (is_spec)
2569 {
2570 if(CS(this).impulse == 12 || CS(this).impulse == 16 || CS(this).impulse == 19 || (CS(this).impulse >= 220 && CS(this).impulse <= 229))
2571 {
2572 this.flags &= ~FL_JUMPRELEASED;
2573 if (!SpectatePrev(this))
2574 {
2575 TRANSMUTE(Observer, this);
2576 PutClientInServer(this);
2577 }
2578 CS(this).impulse = 0;
2579 }
2580 else if(PHYS_INPUT_BUTTON_ATCK2(this))
2581 {
2583 {
2584 this.would_spectate = false;
2585 this.flags &= ~FL_JUMPRELEASED;
2586 TRANSMUTE(Observer, this);
2587 PutClientInServer(this);
2588 }
2589 }
2590 else if(!SpectateUpdate(this) && !SpectateNext(this))
2591 {
2592 PutObserverInServer(this, false, true);
2593 this.would_spectate = true;
2594 }
2595 }
2596 else
2597 {
2598 bool wouldclip = CS_CVAR(this).cvar_cl_clippedspectating;
2599 if (PHYS_INPUT_BUTTON_USE(this))
2600 wouldclip = !wouldclip;
2601 int preferred_movetype = (wouldclip ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
2602 set_movetype(this, preferred_movetype);
2603 }
2604 }
2605 else
2606 { // jump pressed
2607 if ((is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)))
2608 || (!is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))))
2609 {
2610 this.flags |= FL_JUMPRELEASED;
2611 // primary attack pressed
2612 if(this.flags & FL_SPAWNING)
2613 {
2614 this.flags &= ~FL_SPAWNING;
2615 if(joinAllowed(this, this.wants_join))
2616 Join(this, teamplay);
2617 else if(time < CS(this).jointime + MIN_SPEC_TIME)
2618 CS(this).autojoin_checked = -1;
2619 return;
2620 }
2621 }
2622 if(is_spec && !SpectateUpdate(this))
2623 PutObserverInServer(this, false, true);
2624 }
2625 if (is_spec)
2626 this.flags |= FL_CLIENT | FL_NOTARGET;
2627}
2628
2630{
2631 return (IS_VEHICLE(e) && !IS_DEAD(e) && e.takedamage != DAMAGE_NO
2632 && (!e.owner || ((e.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(e.owner, WarpZone_SearchInRadius_otherent))));
2633}
2635{
2636 if (!IS_PLAYER(this))
2637 return;
2638
2639 if(this.vehicle)
2640 {
2641 if(!game_stopped)
2642 {
2644 return;
2645 }
2646 }
2647 else if(autocvar_g_vehicles_enter)
2648 {
2649 if(!game_stopped && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
2650 {
2653 entity closest = NULL;
2654 float head_dist2, closest_dist2 = 0;
2655 for (; head; head = head.chain) // find the closest acceptable target to enter
2656 {
2657 head_dist2 = vlen2(this.origin - WarpZone_UnTransformOrigin(head, head.origin));
2658 if (!closest || head_dist2 < closest_dist2) // new closest
2659 {
2660 closest_dist2 = head_dist2;
2661 closest = head;
2662 }
2663 }
2664
2665 if (closest)
2666 {
2667 vehicles_enter(this, closest);
2668 return;
2669 }
2670 }
2671 }
2672
2673 // a use key was pressed; call handlers
2675}
2676
2677
2678/*
2679=============
2680PlayerPreThink
2681
2682Called every frame for each real client by DP (and for each bot by StartFrame()),
2683and when executing every asynchronous move, so only include things that MUST be done then.
2684Use PlayerFrame() instead for code that only needs to run once per server frame.
2685frametime == 0 in the asynchronous code path.
2686
2687TODO: move more stuff from here and PlayerThink() and ObserverOrSpectatorThink() to PlayerFrame() (frametime is always set there)
2688=============
2689*/
2692{
2694
2695 zoomstate_set = false;
2696
2698
2699 if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed)
2700 PlayerUseKey(this);
2701 CS(this).usekeypressed = PHYS_INPUT_BUTTON_USE(this);
2702
2703 if (IS_PLAYER(this))
2704 {
2705 if (IS_REAL_CLIENT(this) && time < CS(this).jointime + MIN_SPEC_TIME)
2706 error("Client can't be spawned as player on connection!");
2707 if(!PlayerThink(this))
2708 return;
2709 }
2711 {
2713 IntermissionThink(this);
2714 return;
2715 }
2716 else if (IS_REAL_CLIENT(this) && CS(this).autojoin_checked <= 0 && time >= CS(this).jointime + MIN_SPEC_TIME
2717 && this.ping > 0) // delay until centreprints could work (still on loading screen but timers start when it disappears)
2718 {
2719 bool early_join_requested = (CS(this).autojoin_checked < 0);
2720 CS(this).autojoin_checked = 1;
2721 // don't do this in ClientConnect
2722 // many things can go wrong if a client is spawned as player on connection
2723 if (early_join_requested || MUTATOR_CALLHOOK(AutoJoinOnConnection, this)
2725 {
2726 if(joinAllowed(this, this.wants_join))
2727 Join(this, teamplay);
2728 else if (!autocvar_sv_spectate) // we really want to join and some blockers may be brief (g_maxping)
2729 if (time < CS(this).spectatortime + MIN_SPEC_TIME) // centreprints don't appear while spamming
2730 CS(this).autojoin_checked = -1; // keep trying for MIN_SPEC_TIME
2731 return;
2732 }
2733 }
2734 else if (IS_OBSERVER(this) || IS_SPEC(this))
2736
2737 // WEAPONTODO: Add weapon request for this
2738 if (!zoomstate_set)
2739 {
2740 bool wep_zoomed = false;
2741 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
2742 {
2743 .entity weaponentity = weaponentities[slot];
2744 Weapon thiswep = this.(weaponentity).m_weapon;
2745 if(thiswep != WEP_Null && thiswep.wr_zoom)
2746 wep_zoomed += thiswep.wr_zoom(thiswep, this);
2747 }
2748 SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed);
2749 }
2750
2751 // Voice sound effects
2752 if (CS(this).teamkill_soundtime && time > CS(this).teamkill_soundtime)
2753 {
2754 CS(this).teamkill_soundtime = 0;
2755
2756 entity e = CS(this).teamkill_soundsource;
2757 entity oldpusher = e.pusher;
2758 e.pusher = this;
2759 PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY, 1);
2760 e.pusher = oldpusher;
2761 }
2762
2763 if (CS(this).taunt_soundtime && time > CS(this).taunt_soundtime)
2764 {
2765 CS(this).taunt_soundtime = 0;
2766 PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT, 1);
2767 }
2768
2770}
2771
2773{
2774 // TODO: mutator hook to prevent drowning?
2775 if(IS_DEAD(this) || game_stopped || time < game_starttime || this.vehicle
2776 || STAT(FROZEN, this) || this.watertype != CONTENT_WATER)
2777 {
2778 STAT(AIR_FINISHED, this) = 0;
2779 return;
2780 }
2781
2782 if (this.waterlevel != WATERLEVEL_SUBMERGED)
2783 {
2784 if(STAT(AIR_FINISHED, this) && STAT(AIR_FINISHED, this) < time)
2785 PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND, 1);
2786 STAT(AIR_FINISHED, this) = 0;
2787 }
2788 else
2789 {
2790 if (!STAT(AIR_FINISHED, this))
2791 STAT(AIR_FINISHED, this) = time + autocvar_g_balance_contents_drowndelay;
2792 if (STAT(AIR_FINISHED, this) < time)
2793 { // drown!
2794 if (this.pain_finished < time)
2795 {
2797 this.pain_finished = time + 0.5;
2798 }
2799 }
2800 }
2801}
2802
2804
2806{
2808
2809 if(!this.move_qcphysics)
2810 return;
2811
2812 if(!frametime && !CS(this).pm_frametime)
2813 return;
2814
2815 Movetype_Physics_NoMatchTicrate(this, CS(this).pm_frametime, true);
2816}
2817
2818/*
2819=============
2820PlayerPostThink
2821
2822Called every frame for each real client by DP (and for each bot by StartFrame()),
2823and when executing every asynchronous move, so only include things that MUST be done then.
2824Use PlayerFrame() instead for code that only needs to run once per server frame.
2825frametime == 0 in the asynchronous code path.
2826=============
2827*/
2829{
2830 Player_Physics(this);
2831
2832 if (IS_PLAYER(this))
2833 {
2834 DrownPlayer(this);
2835 UpdateChatBubble(this);
2836 if (CS(this).impulse)
2837 ImpulseCommands(this);
2838 GetPressedKeys(this);
2839 }
2840 else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
2841 {
2842 CS(this).pressedkeys = 0;
2843 STAT(PRESSED_KEYS, this) = 0;
2844 }
2845
2847 CS(this).pm_frametime = 0; // CSQCModel_CheckUpdate() and Player_Physics() both read it
2848}
2849
2851{
2852 return (IS_VEHICLE(e) && !IS_DEAD(e) && e.takedamage != DAMAGE_NO);
2853}
2854/*
2855=============
2856PlayerFrame
2857
2858Called every frame for each client by StartFrame().
2859Use this for code that only needs to run once per server frame.
2860frametime is always set here.
2861=============
2862*/
2864{
2865 Physics_UpdateStats(this);
2866
2867 // Don't accumulate alivetime whilst afk as xonstat skill ratings are based on score per second.
2868 // Time accumulated so far (including the 30s) is retained in the event of a move to spec, disconnect, etc.
2869 if (this.alivetime_start && time - CS(this).parm_idlesince >= 30)
2870 this.alivetime_start += frametime;
2871
2872// formerly PreThink code
2873
2874 if (this.score_frame_dmg)
2875 {
2877 GameRules_scoring_add(this, DMG, this.score_frame_dmg);
2878 this.score_frame_dmg = 0;
2879 }
2880 if (this.score_frame_dmgtaken)
2881 {
2883 GameRules_scoring_add(this, DMGTAKEN, this.score_frame_dmgtaken);
2884 this.score_frame_dmgtaken = 0;
2885 }
2886
2887 STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO
2888 STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump;
2889
2890 // physics frames: update anticheat stuff
2891 anticheat_prethink(this);
2892
2893 // Check if spectating is allowed
2894 // cvar hook/callback TODO: make this event-driven
2896 && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this))
2897 {
2898 float cutoff = CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime;
2899 if (time > cutoff + MIN_SPEC_TIME * 0.5 // sv_spectate was disabled recently (or the server was stalled far too long)
2900 || CS(this).autojoin_checked == 0) // or too soon to have tried to autojoin
2901 {
2902 CS(this).spectatortime = time; // reset the grace period
2903 if (CS(this).autojoin_checked) // only notify when sv_spectate was disabled recently, to prevent spam
2904 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
2905 }
2906 else if (time > cutoff)
2907 if (dropclient_schedule(this))
2908 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
2909 }
2910
2911 // Check for nameless players
2912 if (this.netname == "" || this.netname != CS(this).netname_previous)
2913 {
2914 bool assume_unchanged = (CS(this).netname_previous == "");
2915 if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength)
2916 {
2917 int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol);
2918 this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7"));
2919 sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength));
2920 assume_unchanged = false;
2921 // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
2922 }
2923 if (isInvisibleString(this.netname))
2924 {
2925 this.netname = strzone(sprintf("Player#%d", this.playerid));
2926 sprint(this, "Warning: invisible names are not allowed.\n");
2927 assume_unchanged = false;
2928 // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
2929 }
2930 if (!assume_unchanged && autocvar_sv_eventlog)
2931 GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false)));
2932 strcpy(CS(this).netname_previous, this.netname);
2933 }
2934
2935 // version nagging
2936 if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime)
2937 {
2938 CS(this).version_nagtime = 0;
2939 if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0)
2940 {
2941 // git client
2942 }
2943 else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0)
2944 {
2945 // git server
2946 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2947 }
2948 else
2949 {
2950 int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
2951 if (r < 0) // old client
2952 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2953 else if (r > 0) // old server
2954 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
2955 }
2956 }
2957
2958 if (this.ignore_list_send_time > 0 && time > this.ignore_list_send_time)
2959 ignore_list_send(this);
2960
2961 // GOD MODE info
2962 if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
2963 {
2964 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
2965 this.max_armorvalue = 0;
2966 }
2967
2968 // Vehicles
2969 if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle)
2970 if(IS_PLAYER(this) && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
2971 {
2973 {
2974 if (!head.owner)
2975 {
2976 if (!head.team || SAME_TEAM(this, head))
2977 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
2978 else if (autocvar_g_vehicles_steal)
2979 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
2980 }
2981 else if ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this))
2982 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
2983 }
2984
2985 this.last_vehiclecheck = time + 1;
2986 }
2987
2988
2989
2990// formerly PostThink code
2992 if (IS_REAL_CLIENT(this))
2994 if (!intermission_running) // NextLevel() kills all centerprints after setting this true
2995 {
2996 int totalClients = 0;
2998 {
2999 // maxidle disabled in local matches by not counting clients (totalClients 0)
3001 {
3003 {
3004 ++totalClients;
3005 });
3006 if (maxclients - totalClients > autocvar_sv_maxidle_slots)
3007 totalClients = 0;
3008 }
3009 }
3010 else if ((IS_PLAYER(this) || this.wants_join) && autocvar_sv_maxidle_playertospectator > 0)
3011 {
3013 {
3014 ++totalClients;
3015 });
3016 }
3017
3018 if (totalClients < autocvar_sv_maxidle_minplayers)
3019 {
3020 // idle kick disabled
3021 CS(this).parm_idlesince = time;
3022 }
3023 else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
3024 {
3025 if (CS(this).idlekick_lasttimeleft)
3026 {
3027 CS(this).idlekick_lasttimeleft = 0;
3028 Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
3029 }
3030 }
3031 else
3032 {
3033 float maxidle_time = autocvar_sv_maxidle;
3034 if ((IS_PLAYER(this) || this.wants_join)
3037 float timeleft = ceil(maxidle_time - (time - CS(this).parm_idlesince));
3038 float countdown_time = max(min(10, maxidle_time - 1), ceil(maxidle_time * 0.33)); // - 1 to support maxidle_time <= 10
3039 if (timeleft == countdown_time && !CS(this).idlekick_lasttimeleft)
3040 {
3042 {
3043 if (!this.wants_join) // no countdown centreprint when getting kicked off the join queue
3044 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOVETOSPEC_IDLING, timeleft);
3045 }
3046 else
3047 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
3048 }
3049 if (timeleft <= 0)
3050 {
3051 if ((IS_PLAYER(this) || this.wants_join)
3053 {
3054 if (this.wants_join)
3055 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING_QUEUE, this.netname, maxidle_time);
3056 else
3057 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING, this.netname, maxidle_time);
3058 PutObserverInServer(this, true, true);
3059 // Can't do this in PutObserverInServer() or SetPlayerTeam() cos it causes
3060 // mouse2 (change spectate mode) to kick the player off the join queue.
3061 this.wants_join = 0;
3062 this.team_selected = 0;
3063 // when the player is kicked off the server, these are called in ClientDisconnect()
3067 }
3068 else
3069 {
3070 if (dropclient_schedule(this))
3071 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname, maxidle_time);
3072 }
3073 return;
3074 }
3075 else if (timeleft <= countdown_time
3076 && !this.wants_join) // no countdown bangs when getting kicked off the join queue
3077 {
3078 if (timeleft != CS(this).idlekick_lasttimeleft)
3079 play2(this, SND(TALK2));
3080 CS(this).idlekick_lasttimeleft = timeleft;
3081 }
3082 }
3083 }
3084
3085 CheatFrame(this);
3086
3087 if (game_stopped)
3088 {
3089 this.solid = SOLID_NOT;
3090 this.takedamage = DAMAGE_NO;
3092 CS(this).teamkill_complain = 0;
3093 CS(this).teamkill_soundtime = 0;
3094 CS(this).teamkill_soundsource = NULL;
3095 }
3096
3098 float hp = healtharmor_maxdamage(GetResource(this, RES_HEALTH), GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x;
3100 }
3101}
3102
3103// hack to copy the button fields from the client entity to the Client State
3105{
3106 if(this.impulse)
3107 store.impulse = this.impulse;
3108 this.impulse = 0;
3109
3110 bool typing = this.buttonchat || this.button12;
3111
3112 store.button0 = (typing) ? 0 : this.button0;
3113 //button1?!
3114 store.button2 = (typing) ? 0 : this.button2;
3115 store.button3 = (typing) ? 0 : this.button3;
3116 store.button4 = this.button4;
3117 store.button5 = (typing) ? 0 : this.button5;
3118 store.button6 = this.button6;
3119 store.button7 = this.button7;
3120 store.button8 = this.button8;
3121 store.button9 = this.button9;
3122 store.button10 = this.button10;
3123 store.button11 = this.button11;
3124 store.button12 = this.button12;
3125 store.button13 = this.button13;
3126 store.button14 = this.button14;
3127 store.button15 = this.button15;
3128 store.button16 = this.button16;
3129 store.buttonuse = this.buttonuse;
3130 store.buttonchat = this.buttonchat;
3131
3132 store.cursor_active = this.cursor_active;
3133 store.cursor_screen = this.cursor_screen;
3134 store.cursor_trace_start = this.cursor_trace_start;
3135 store.cursor_trace_endpos = this.cursor_trace_endpos;
3136 store.cursor_trace_ent = this.cursor_trace_ent;
3137
3138 store.ping = this.ping;
3139 store.ping_packetloss = this.ping_packetloss;
3140 store.ping_movementloss = this.ping_movementloss;
3141
3142 store.v_angle = this.v_angle;
3143 store.movement = this.movement;
3144}
3145
3146NET_HANDLE(fpsreport, bool)
3147{
3148 int fps = ReadShort();
3149 PlayerScore_Set(sender, SP_FPS, fps);
3150 return true;
3151}
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:113
int autocvar_g_antilag
Definition antilag.qh:5
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:708
bool drag_undraggable(entity draggee, entity dragger)
Definition cheats.qc:901
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:37
virtual void wr_zoom()
(BOTH) weapon specific zoom reticle
Definition weapon.qh:142
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:2180
vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
Definition util.qc:1391
Notification Announcer_PickNumber(int type, int num)
Definition util.qc:1926
float compressShotOrigin(vector v)
Definition util.qc:1350
float textLengthUpToLength(string theText, int maxLength, textLengthUpToLength_lenFunction_t w)
Definition util.qc:920
float get_model_parameters(string m, float sk)
Definition util.qc:1494
string mi_shortname
Definition util.qh:133
float get_model_parameters_species
Definition util.qh:187
string strtolower(string s)
const int CNT_RESPAWN
Definition util.qh:262
const int FL_CLIENT
Definition constants.qh:64
const int KEY_JUMP
Definition constants.qh:40
const int KEY_ATCK2
Definition constants.qh:43
const int KEY_RIGHT
Definition constants.qh:39
const int FL_PICKUPITEMS
Definition constants.qh:80
const int FL_NOTARGET
Definition constants.qh:68
const int FL_JUMPRELEASED
Definition constants.qh:73
const int KEY_BACKWARD
Definition constants.qh:37
const int FL_SPAWNING
Definition constants.qh:79
const int HUD_NORMAL
Definition constants.qh:48
const int KEY_FORWARD
Definition constants.qh:36
const int FL_GODMODE
Definition constants.qh:67
const int KEY_LEFT
Definition constants.qh:38
const int KEY_CROUCH
Definition constants.qh:41
const int FRAGS_PLAYER
not 0 because all fields are initially 0
Definition constants.qh:3
const int KEY_ATCK
Definition constants.qh:42
const int SPECIES_HUMAN
Definition constants.qh:23
const int FRAGS_SPECTATOR
engine expects this value
Definition constants.qh:5
#define EFMASK_CHEAP
Definition constants.qh:101
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 MOVE_NOMONSTERS
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:484
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:155
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(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_SearchInRadius(vector org, float rad, float nomonsters, WarpZone_FindRadius_cond_callback_t cb)
Modified findradius aware of warpzones, that only includes entities that meet the "cb" criteria.
Definition common.qc:684
vector WarpZone_UnTransformOrigin(entity wz, vector org)
Transforms origin org backwards through warpzone wz, as the inverse of WarpZone_TransformOrigin.
Definition common.qc:530
noref entity WarpZone_SearchInRadius_otherent
Free to use in the callback.
Definition common.qh:71
#define MOVE_NOTHING
Definition common.qh:35
#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:1387
void MapInfo_ClearTemps()
Definition mapinfo.qc:1635
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:14
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 NAME CPID_PREVENT_JOIN
Definition all.inc:743
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:696
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:1540
int player_getspecies(entity this)
Definition client.qc:444
bool SpectateSet(entity this)
Definition client.qc:1884
void play_countdown(entity this, float finished, Sound samp)
Definition client.qc:1531
entity chatbubbleentity
Definition client.qc:1267
void SpectateCopy(entity this, entity spectatee)
Definition client.qc:1798
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:1636
void GiveWarmupResources(entity this)
Definition client.qc:568
entity SelectObservePoint(entity this)
Definition client.qc:250
bool ShowTeamSelection(entity this)
Definition client.qc:2061
int GetPlayerLimit()
Definition client.qc:2154
bool SpectateUpdate(entity this)
Definition client.qc:1866
void DrownPlayer(entity this)
Definition client.qc:2772
void GetPressedKeys(entity this)
Definition client.qc:1766
void PlayerUseKey(entity this)
Definition client.qc:2634
float autocvar_g_maxping
Definition client.qc:2262
void ClientData_Detach(entity this)
Definition client.qc:179
void player_powerups(entity this)
Definition client.qc:1556
void SetNewParms()
Definition client.qc:961
void SendWelcomeMessage(entity this, int msg_type)
Definition client.qc:1078
bool zoomstate_set
Definition client.qc:1755
bool PlayerUseKey_EnterVehicle_cond(entity e)
Definition client.qc:2629
void UpdateChatBubble(entity this)
Definition client.qc:1376
void FixClientCvars(entity e)
Definition client.qc:1000
bool PlayerInIDList(entity p, string idlist)
Definition client.qc:1039
bool would_spectate
Definition client.qc:2508
void SetZoomState(entity this, float newzoom)
Definition client.qc:1756
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
bool Vehicle_Enter_cond(entity e)
Definition client.qc:2850
void player_regen(entity this)
Definition client.qc:1687
float last_vehiclecheck
Definition client.qc:2690
void Player_Physics(entity this)
Definition client.qc:2805
bool SpectateNext(entity this)
Definition client.qc:1986
void ChatBubbleThink(entity this)
Definition client.qc:1349
void respawn(entity this)
Definition client.qc:1501
void PutPlayerInServer(entity this)
Definition client.qc:580
void calculate_player_respawn_time(entity this)
Definition client.qc:1398
float CalcRot(float current, float stable, float rotfactor, float rotframetime)
Definition client.qc:1646
void ClientInit_CheckUpdate(entity this)
Definition client.qc:932
bool PlayerThink(entity this)
Definition client.qc:2349
bool ClientInit_SendEntity(entity this, entity to, int sf)
Definition client.qc:897
void ObserverOrSpectatorThink(entity this)
Definition client.qc:2510
bool PlayerInIPList(entity p, string iplist)
Definition client.qc:1030
bool queuePlayer(entity this, int team_index)
Definition client.qc:2214
void ShowRespawnCountdown(entity this)
Definition client.qc:2042
int nJoinAllowed(entity this)
Determines how many player slots are free.
Definition client.qc:2176
void PM_UpdateButtons(entity this, entity store)
Definition client.qc:3104
void SetSpectatee_status(entity this, int spectatee_num)
Definition client.qc:1903
bool Spectate(entity this, entity pl)
Definition client.qc:1976
bool joinAllowed(entity this, int team_index)
Definition client.qc:2263
bool dualwielding_prev
Definition client.qc:2348
bool findinlist_abbrev(string tofind, string list)
Definition client.qc:1016
void FixPlayermodel(entity player)
Definition client.qc:454
bool SpectatePrev(entity this)
Definition client.qc:2000
void PlayerFrame(entity this)
Definition client.qc:2863
bool PlayerInList(entity player, string list)
Definition client.qc:1048
void ClientInit_Spawn()
Definition client.qc:947
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:1656
void DecodeLevelParms(entity this)
Definition client.qc:987
void show_entnum(entity this)
Definition client.qc:2322
string shootfromfixedorigin
Definition client.qc:1266
void Join(entity this, bool queued_join)
it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
Definition client.qc:2073
void ClientInit_misc(entity this)
Definition client.qc:909
void SetSpectatee(entity this, entity spectatee)
Definition client.qc:1938
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_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_foginterval
Definition client.qh:36
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:979
@ MUT_SPECPREV_CONTINUE
Definition events.qh:978
@ MUT_SPECPREV_FOUND
Definition events.qh:980
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:35
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:987
#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:694
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition teamplay.qc:528
void TeamBalance_RemoveExcessPlayers(entity ignore)
Definition teamplay.qc:631
bool QueueNeeded(entity client, int team_index)
Definition teamplay.qc:1254
void TeamBalance_JoinBestTeam(entity player)
Assigns the given player to a team that will make the game most balanced.
Definition teamplay.qc:415
bool Player_HasRealForcedTeam(entity player)
Returns whether player has real forced team.
Definition teamplay.qc:340
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:370
int TeamBalance_GetAllowedTeams(entity balance)
Returns the bitmask of allowed teams.
Definition teamplay.qc:541
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:424
bool TeamBalance_AreEqual(entity ignore, bool would_leave)
Definition teamplay.qc:585
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:892
string autocvar_g_forced_team_otherwise
Definition teamplay.qh:16
@ TEAM_FORCE_DEFAULT
Don't force any team.
Definition teamplay.qh:153
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:576
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:94
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 WriteWepSet(float dst, WepSet w)
Definition all.qc:69
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:444
entity exteriorweaponentity
Definition all.qh:403
#define WEP_CVAR(wep, name)
Definition all.qh:339
WepSet WEPSET_SUPERWEAPONS
Definition all.qh:347
vector spawnorigin
Definition all.qh:418
@ WEPARENA_OFF
Definition weapon.qh:273
@ WEPARENA_CUSTOM
Definition weapon.qh:275
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
const int WEP_FLAG_RELOADABLE
can has reload
Definition weapon.qh:254
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:79
float warmup_start_ammo_cells
Definition world.qh:104
string clientstuff
Definition world.qh:61
#define WARMUP_START_WEAPONS
Definition world.qh:100
entity random_start_ammo
Entity that contains amount of ammo to give with random start weapons.
Definition world.qh:94
string autocvar_g_random_start_weapons
Holds a list of possible random start weapons.
Definition world.qh:91
bool autocvar_g_jetpack
Definition world.qh:8
float start_ammo_shells
Definition world.qh:83
float warmup_start_ammo_rockets
Definition world.qh:103
float warmup_start_ammo_shells
Definition world.qh:101
float start_ammo_fuel
Definition world.qh:87
int start_items
Definition world.qh:82
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:89
float warmup_start_ammo_nails
Definition world.qh:102
float default_weapon_alpha
Definition world.qh:73
WepSet g_weaponarena_weapons
Definition world.qh:75
float default_player_alpha
Definition world.qh:72
float start_ammo_cells
Definition world.qh:86
string cache_lastmutatormsg
Definition world.qh:70
float warmup_start_health
Definition world.qh:106
bool autocvar_sv_dedicated
Definition world.qh:41
float start_ammo_rockets
Definition world.qh:85
float g_weapon_stay
Definition world.qh:108
int g_weaponarena_type
Definition world.qh:77
float start_armorvalue
Definition world.qh:96
int autocvar_g_warmup
Definition world.qh:9
bool observe_blocked_if_eliminated
Definition world.qh:159
float warmup_start_ammo_fuel
Definition world.qh:105
float start_health
Definition world.qh:95
bool sv_ready_restart_after_countdown
Definition world.qh:115
float warmup_start_armorvalue
Definition world.qh:107
float start_ammo_nails
Definition world.qh:84
string cache_mutatormsg
Definition world.qh:69