--- /dev/null
+ #include "infomessages.qh"
+
+ #include <common/ent_cs.qh>
+ #include <common/mapinfo.qh>
+
+ // Info messages panel (#14)
+
+ #define drawInfoMessage(s) MACRO_BEGIN { \
+ if(autocvar_hud_panel_infomessages_flip) \
+ o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \
+ drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \
+ o.y += fontsize.y; \
+ } MACRO_END
+ void HUD_InfoMessages()
+ {
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_infomessages) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ if (autocvar_hud_panel_infomessages_dynamichud)
+ HUD_Scale_Enable();
+ else
+ HUD_Scale_Disable();
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ // always force 5:1 aspect
+ vector newSize = '0 0 0';
+ if(mySize.x/mySize.y > 5)
+ {
+ newSize.x = 5 * mySize.y;
+ newSize.y = mySize.y;
+
+ pos.x = pos.x + (mySize.x - newSize.x) / 2;
+ }
+ else
+ {
+ newSize.y = 1/5 * mySize.x;
+ newSize.x = mySize.x;
+
+ pos.y = pos.y + (mySize.y - newSize.y) / 2;
+ }
+
+ mySize = newSize;
+ entity tm;
+ vector o;
+ o = pos;
+
+ vector fontsize;
+ fontsize = '0.20 0.20 0' * mySize.y;
+
+ float a;
+ a = panel_fg_alpha;
+
+ string s;
+ if(!autocvar__hud_configure)
+ {
+ if(spectatee_status)
+ {
+ a = 1;
+ if(spectatee_status == -1)
+ s = _("^1Observing");
+ else
+ s = sprintf(_("^1Spectating: ^7%s"), entcs_GetName(current_player));
+ drawInfoMessage(s);
+
+ if(spectatee_status == -1)
+ s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
+ else
+ s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
+ drawInfoMessage(s);
+
+ if(spectatee_status == -1)
+ s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
+ else
+ s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2"));
+ drawInfoMessage(s);
+
+ s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
+ drawInfoMessage(s);
+
+ if(gametype == MAPINFO_TYPE_LMS)
+ {
+ entity sk;
+ sk = playerslots[player_localnum];
+ if(sk.(scores[ps_primary]) >= 666)
+ s = _("^1Match has already begun");
+ else if(sk.(scores[ps_primary]) > 0)
+ s = _("^1You have no more lives left");
+ else
+ s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+ }
+ else
+ s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+ drawInfoMessage(s);
+ }
+
+ if (time < STAT(GAMESTARTTIME))
+ {
+ //we need to ceil, otherwise the countdown would be off by .5 when using round()
+ float countdown = ceil(STAT(GAMESTARTTIME) - time);
+ s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
+ drawInfoMessage(s);
+ }
+
+ if(warmup_stage)
+ {
+ s = _("^2Currently in ^1warmup^2 stage!");
+ drawInfoMessage(s);
+ }
+
++ if(autocvar_cl_showspectators)
++ if(num_spectators)
++ //if(spectatee_status != -1)
++ {
++ s = ((spectatee_status) ? _("^1Spectating this player:") : _("^1Spectating you:"));
++ //drawInfoMessage(s)
++ int limit = min(num_spectators, MAX_SPECTATORS);
++ for(int i = 0; i < limit; ++i)
++ {
++ float slot = spectatorlist[i];
++ if(i == 0)
++ s = strcat(s, " ^3", entcs_GetName(slot));
++ else
++ s = strcat("^3", entcs_GetName(slot));
++ drawInfoMessage(s);
++ }
++ }
++
+ string blinkcolor;
+ if(time % 1 >= 0.5)
+ blinkcolor = "^1";
+ else
+ blinkcolor = "^3";
+
+ if(ready_waiting && !spectatee_status)
+ {
+ if(ready_waiting_for_me)
+ {
+ if(warmup_stage)
+ s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+ else
+ s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+ }
+ else
+ {
+ if(warmup_stage)
+ s = _("^2Waiting for others to ready up to end warmup...");
+ else
+ s = _("^2Waiting for others to ready up...");
+ }
+ drawInfoMessage(s);
+ }
+ else if(warmup_stage && !spectatee_status)
+ {
+ s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
+ drawInfoMessage(s);
+ }
+
+ if(teamplay && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
+ {
+ float ts_min = 0, ts_max = 0;
+ tm = teams.sort_next;
+ if (tm)
+ {
+ for (; tm.sort_next; tm = tm.sort_next)
+ {
+ if(!tm.team_size || tm.team == NUM_SPECTATOR)
+ continue;
+ if(!ts_min) ts_min = tm.team_size;
+ else ts_min = min(ts_min, tm.team_size);
+ if(!ts_max) ts_max = tm.team_size;
+ else ts_max = max(ts_max, tm.team_size);
+ }
+ if ((ts_max - ts_min) > 1)
+ {
+ s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
+ tm = GetTeam(myteam, false);
+ if (tm)
+ if (tm.team != NUM_SPECTATOR)
+ if (tm.team_size == ts_max)
+ s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
+ drawInfoMessage(s);
+ }
+ }
+ }
+ }
+ else
+ {
+ s = _("^7Press ^3ESC ^7to show HUD options.");
+ drawInfoMessage(s);
+ s = _("^3Doubleclick ^7a panel for panel-specific options.");
+ drawInfoMessage(s);
+ s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
+ drawInfoMessage(s);
+ s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
+ drawInfoMessage(s);
+ }
+ }
#include "../common/monsters/sv_monsters.qh"
- #include "../warpzonelib/server.qh"
+ #include "../lib/warpzone/server.qh"
+
+ STATIC_METHOD(Client, Add, void(Client this, int _team))
+ {
+ WITHSELF(this, ClientConnect());
+ TRANSMUTE(Player, this);
+ this.frame = 12; // 7
+ this.team = _team;
+ WITHSELF(this, PutClientInServer());
+ }
+
+ void PutObserverInServer(entity this);
+ void ClientDisconnect();
- float c1, c2, c3, c4;
+ STATIC_METHOD(Client, Remove, void(Client this))
+ {
+ TRANSMUTE(Observer, this);
+ WITHSELF(this, PutClientInServer());
+ WITHSELF(this, ClientDisconnect());
+ }
void send_CSQC_teamnagger() {
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
+ WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
}
- entity head;
- float spec_count = 0;
- FOR_EACH_REALCLIENT(head)
+int CountSpectators(entity player, entity to)
+{
+ if(!player) { return 0; } // not sure how, but best to be safe
+
- if(IS_SPEC(head))
- if(head != to)
- if(head.enemy == player)
- spec_count += 1;
- }
++ int spec_count = 0;
++
++ FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
+ {
- entity head;
- FOR_EACH_REALCLIENT(head)
++ spec_count++;
++ });
+
+ return spec_count;
+}
+
+void WriteSpectators(entity player, entity to)
+{
+ if(!player) { return; } // not sure how, but best to be safe
+
- if(IS_SPEC(head))
- if(head != to)
- if(head.enemy == player)
- WriteByte(MSG_ENTITY, num_for_edict(head));
- }
++ FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
+ {
- bool ClientData_Send(entity to, int sf)
++ WriteByte(MSG_ENTITY, num_for_edict(it));
++ });
+}
+
+ bool ClientData_Send(entity this, entity to, int sf)
{
- if(to != self.owner)
- {
- error("wtf");
- return false;
- }
+ assert(to == this.owner, return false);
- entity e;
-
- e = to;
- if(IS_SPEC(to))
- e = to.enemy;
+ entity e = to;
+ if (IS_SPEC(e)) e = e.enemy;
sf = 0;
+ if (e.race_completed) sf |= 1; // forced scoreboard
+ if (to.spectatee_status) sf |= 2; // spectator ent number follows
+ if (e.zoomstate) sf |= 4; // zoomed
+ if (e.porto_v_angle_held) sf |= 8; // angles held
++ sf |= 16; // always check spectators
- if(e.race_completed)
- sf |= 1; // forced scoreboard
- if(to.spectatee_status)
- sf |= 2; // spectator ent number follows
- if(e.zoomstate)
- sf |= 4; // zoomed
- if(e.porto_v_angle_held)
- sf |= 8; // angles held
- // always check spectators
- sf |= 16; // spectator handling?
-
- WriteByte(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
WriteByte(MSG_ENTITY, sf);
- if(sf & 2)
+ if (sf & 2)
+ {
WriteByte(MSG_ENTITY, to.spectatee_status);
-
- if(sf & 8)
+ }
+ if (sf & 8)
{
WriteAngle(MSG_ENTITY, e.v_angle.x);
WriteAngle(MSG_ENTITY, e.v_angle.y);
void setplayermodel(entity e, string modelname)
{
precache_model(modelname);
- setmodel(e, modelname);
- player_setupanimsformodel();
- UpdatePlayerSounds();
+ _setmodel(e, modelname);
+ player_setupanimsformodel(e);
+ if(!autocvar_g_debug_globalsounds)
+ UpdatePlayerSounds(e);
}
- /*
- =============
- PutObserverInServer
-
- putting a client as observer in the server
- =============
- */
- void FixPlayermodel();
- void PutObserverInServer (void)
+ void FixPlayermodel(entity player);
+ /** putting a client as observer in the server */
+ void PutObserverInServer(entity this)
{
- entity spot;
-
- SetSpectator(self, world);
-
- self.hud = HUD_NORMAL;
-
- if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
+ bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
+ PlayerState_detach(this);
- spot = SelectSpawnPoint (true);
- if(!spot)
- error("No spawnpoints for observers?!?\n");
- RemoveGrapplingHook(self); // Wazat's Grappling Hook
-
- if(IS_REAL_CLIENT(self))
- {
- msg_entity = self;
- WriteByte(MSG_ONE, SVC_SETVIEW);
- WriteEntity(MSG_ONE, self);
- }
-
- self.frags = FRAGS_SPECTATOR;
-
- MUTATOR_CALLHOOK(MakePlayerObserver);
+ if (IS_PLAYER(this) && this.health >= 1) {
+ // despawn effect
+ Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+ }
- Portal_ClearAll(self);
+ {
+ entity spot = SelectSpawnPoint(this, true);
+ if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
+ this.angles = spot.angles;
+ this.angles_z = 0;
+ this.fixangle = true;
+ // offset it so that the spectator spawns higher off the ground, looks better this way
+ setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL));
+ this.prevorigin = this.origin;
+ if (IS_REAL_CLIENT(this))
+ {
+ msg_entity = this;
+ WriteByte(MSG_ONE, SVC_SETVIEW);
+ WriteEntity(MSG_ONE, this);
+ }
+ // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
+ // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
+ if(!autocvar_g_debug_globalsounds)
+ {
+ // needed for player sounds
+ this.model = "";
+ FixPlayermodel(this);
+ }
+ setmodel(this, MDL_Null);
+ setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL));
+ this.view_ofs = '0 0 0';
+ }
- Unfreeze(self);
+ RemoveGrapplingHook(this);
+ Portal_ClearAll(this);
+ Unfreeze(this);
++ SetSpectatee(this, world);
- if(self.alivetime)
+ if (this.alivetime)
{
- if(!warmup_stage)
- PS_GR_P_ADDVAL(self, PLAYERSTATS_ALIVETIME, time - self.alivetime);
- self.alivetime = 0;
+ if (!warmup_stage)
+ PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+ this.alivetime = 0;
}
- if(self.vehicle)
- vehicles_exit(VHEF_RELESE);
+ if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
- WaypointSprite_PlayerDead();
+ WaypointSprite_PlayerDead(this);
- if (!g_ca) // don't reset teams when moving a ca player to the spectators
- self.team = -1; // move this as it is needed to log the player spectating in eventlog
+ if (mutator_returnvalue) {
+ // mutator prevents resetting teams+score
+ } else {
+ this.team = -1; // move this as it is needed to log the player spectating in eventlog
+ this.frags = FRAGS_SPECTATOR;
+ PlayerScore_Clear(this); // clear scores when needed
+ }
- if(self.killcount != -666)
+ if (this.killcount != FRAGS_SPECTATOR)
{
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname);
- if(autocvar_g_chat_nospectators == 1 || (cvar("g_warmup") && !(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
- Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CHAT_NOSPECTATORS);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
+ if(!intermission_running)
+ if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
- if(self.just_joined == false) {
- LogTeamchange(self.playerid, -1, 4);
+ if(this.just_joined == false) {
+ LogTeamchange(this.playerid, -1, 4);
} else
- self.just_joined = false;
- }
-
- PlayerScore_Clear(self); // clear scores when needed
-
- accuracy_resend(self);
-
- self.spectatortime = time;
-
- self.classname = "observer";
- self.iscreature = false;
- self.teleportable = TELEPORT_SIMPLE;
- self.damagedbycontents = false;
- self.health = -666;
- self.takedamage = DAMAGE_NO;
- self.solid = SOLID_NOT;
- self.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
- self.flags = FL_CLIENT | FL_NOTARGET;
- self.armorvalue = 666;
- self.effects = 0;
- self.armorvalue = autocvar_g_balance_armor_start;
- self.pauserotarmor_finished = 0;
- self.pauserothealth_finished = 0;
- self.pauseregen_finished = 0;
- self.damageforcescale = 0;
- self.death_time = 0;
- self.respawn_flags = 0;
- self.respawn_time = 0;
- self.stat_respawn_time = 0;
- self.alpha = 0;
- self.scale = 0;
- self.fade_time = 0;
- self.pain_frame = 0;
- self.pain_finished = 0;
- self.strength_finished = 0;
- self.invincible_finished = 0;
- self.superweapons_finished = 0;
- self.pushltime = 0;
- self.istypefrag = 0;
- self.think = func_null;
- self.nextthink = 0;
- self.hook_time = 0;
- self.deadflag = DEAD_NO;
- self.angles = spot.angles;
- self.angles_z = 0;
- self.fixangle = true;
- self.crouch = false;
- self.revival_time = 0;
-
- setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
- self.prevorigin = self.origin;
- self.items = 0;
- self.weapons = '0 0 0';
- self.model = "";
- FixPlayermodel();
- setmodel(self, "null");
- self.drawonlytoclient = self;
-
- setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
- self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
-
- self.weapon = 0;
- self.weaponname = "";
- self.switchingweapon = 0;
- self.weaponmodel = "";
- self.weaponentity = world;
- self.exteriorweaponentity = world;
- self.killcount = -666;
- self.velocity = '0 0 0';
- self.avelocity = '0 0 0';
- self.punchangle = '0 0 0';
- self.punchvector = '0 0 0';
- self.oldvelocity = self.velocity;
- self.fire_endtime = -1;
- self.event_damage = func_null;
+ this.just_joined = false;
+ }
+
+ accuracy_resend(this);
+
+ this.spectatortime = time;
+ this.bot_attack = false;
+ this.hud = HUD_NORMAL;
+ TRANSMUTE(Observer, this);
+ this.iscreature = false;
+ this.teleportable = TELEPORT_SIMPLE;
+ this.damagedbycontents = false;
+ this.health = FRAGS_SPECTATOR;
+ this.takedamage = DAMAGE_NO;
+ this.solid = SOLID_NOT;
+ this.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
+ this.flags = FL_CLIENT | FL_NOTARGET;
+ this.armorvalue = 666;
+ this.effects = 0;
+ this.armorvalue = autocvar_g_balance_armor_start;
+ this.pauserotarmor_finished = 0;
+ this.pauserothealth_finished = 0;
+ this.pauseregen_finished = 0;
+ this.damageforcescale = 0;
+ this.death_time = 0;
+ this.respawn_flags = 0;
+ this.respawn_time = 0;
+ this.stat_respawn_time = 0;
+ this.alpha = 0;
+ this.scale = 0;
+ this.fade_time = 0;
+ this.pain_frame = 0;
+ this.pain_finished = 0;
+ this.strength_finished = 0;
+ this.invincible_finished = 0;
+ this.superweapons_finished = 0;
+ this.pushltime = 0;
+ this.istypefrag = 0;
+ setthink(this, func_null);
+ this.nextthink = 0;
+ this.hook_time = 0;
+ this.deadflag = DEAD_NO;
+ this.crouch = false;
+ this.revival_time = 0;
+
+ this.items = 0;
+ this.weapons = '0 0 0';
+ this.drawonlytoclient = this;
+
+ this.weaponname = "";
+ this.weaponmodel = "";
+ for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ this.weaponentities[slot] = NULL;
+ }
+ this.exteriorweaponentity = NULL;
+ this.killcount = FRAGS_SPECTATOR;
+ this.velocity = '0 0 0';
+ this.avelocity = '0 0 0';
+ this.punchangle = '0 0 0';
+ this.punchvector = '0 0 0';
+ this.oldvelocity = this.velocity;
+ this.fire_endtime = -1;
+ this.event_damage = func_null;
}
- .float model_randomizer;
- void FixPlayermodel()
+ int player_getspecies(entity this)
{
- string defaultmodel;
- float defaultskin, chmdl, oldskin, n, i;
- vector m1, m2;
-
- defaultmodel = "";
- defaultskin = 0;
- chmdl = false;
+ get_model_parameters(this.model, this.skin);
+ int s = get_model_parameters_species;
+ get_model_parameters(string_null, 0);
+ if (s < 0) return SPECIES_HUMAN;
+ return s;
+ }
- if(autocvar_sv_defaultcharacter == 1)
+ .float model_randomizer;
+ void FixPlayermodel(entity player)
+ {
+ string defaultmodel = "";
+ int defaultskin = 0;
+ if(autocvar_sv_defaultcharacter)
{
if(teamplay)
{
*/
.entity chatbubbleentity;
void ReadyCount();
- void ClientDisconnect (void)
- {
- if(self.vehicle)
- vehicles_exit(VHEF_RELESE);
-
- if (!IS_CLIENT(self))
- {
- print("Warning: ClientDisconnect without ClientConnect\n");
- return;
- }
-
- PlayerStats_GameReport_FinalizePlayer(self);
-
- SetSpectator(self, world);
-
- if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
+ void ClientDisconnect()
+ {ENGINE_EVENT();
+ assert(IS_CLIENT(this), return);
- CheatShutdownClient();
+ PlayerStats_GameReport_FinalizePlayer(this);
+ if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+ if (this.active_minigame) part_minigame(this);
+ if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
- W_HitPlotClose(self);
+ if (autocvar_sv_eventlog)
+ GameLogEcho(strcat(":part:", ftos(this.playerid)));
- anticheat_report();
- anticheat_shutdown();
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
- playerdemo_shutdown();
++ SetSpectatee(this, NULL);
+
- bot_clientdisconnect();
+ MUTATOR_CALLHOOK(ClientDisconnect, this);
- if(self.entcs)
- detach_entcs();
+ ClientState_detach(this);
- if(autocvar_sv_eventlog)
- GameLogEcho(strcat(":part:", ftos(self.playerid)));
-
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_DISCONNECT, self.netname);
-
- MUTATOR_CALLHOOK(ClientDisconnect);
+ Portal_ClearAll(this);
- Portal_ClearAll(self);
+ Unfreeze(this);
- Unfreeze(self);
-
- RemoveGrapplingHook(self);
+ RemoveGrapplingHook(this);
// Here, everything has been done that requires this player to be a client.
}
}
- float SpectateUpdate()
+ bool SpectateUpdate(entity this)
{
- if(!self.enemy)
- return 0;
+ if(!this.enemy)
+ return false;
- if(!IS_PLAYER(self.enemy) || self == self.enemy)
+ if(!IS_PLAYER(this.enemy) || this == this.enemy)
{
- SetSpectator(self, world);
- return 0;
+ SetSpectatee(this, NULL);
+ return false;
}
- SpectateCopy(self.enemy);
+ SpectateCopy(this, this.enemy);
- return 1;
+ return true;
}
- float SpectateSet()
+ bool SpectateSet(entity this)
{
- if(!IS_PLAYER(self.enemy))
+ if(!IS_PLAYER(this.enemy))
return false;
- ClientData_Touch(self.enemy);
++ ClientData_Touch(this.enemy);
+
- msg_entity = self;
+ msg_entity = this;
WriteByte(MSG_ONE, SVC_SETVIEW);
- WriteEntity(MSG_ONE, self.enemy);
- //stuffcmd(self, "set viewsize $tmpviewsize \n");
- self.movetype = MOVETYPE_NONE;
- accuracy_resend(self);
+ WriteEntity(MSG_ONE, this.enemy);
+ this.movetype = MOVETYPE_NONE;
+ accuracy_resend(this);
- if(!SpectateUpdate())
- PutObserverInServer();
+ if(!SpectateUpdate(this))
+ PutObserverInServer(this);
return true;
}
// WEAPONTODO
// these are required to fix the spectator bug with arc
if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
- if(player.enemy && player.enemy.arc_beam) { player.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
+ if(this.enemy && this.enemy.arc_beam) { this.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
+
+ // needed to update spectator list
+ if(old_spectatee) { ClientData_Touch(old_spectatee); }
}
- float Spectate(entity pl)
- {
- if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
- if(DIFF_TEAM(pl, self))
- return 0;
-
- SetSpectator(self, pl);
- return SpectateSet();
- }
-
- // Returns next available player to spectate if g_ca_spectate_enemies == 0
- entity CA_SpectateNext(entity start)
+ bool Spectate(entity this, entity pl)
{
- if(SAME_TEAM(start, self)) { return start; }
-
- other = start;
- // continue from current player
- while(other && DIFF_TEAM(other, self))
- other = find(other, classname, "player");
-
- if (!other)
- {
- // restart from begining
- other = find(other, classname, "player");
- while(other && DIFF_TEAM(other, self))
- other = find(other, classname, "player");
- }
+ if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
+ return false;
+ pl = M_ARGV(1, entity);
- return other;
+ SetSpectatee(this, pl);
+ return SpectateSet(this);
}
- float SpectateNext()
+ bool SpectateNext(entity this)
{
- other = find(self.enemy, classname, "player");
+ other = find(this.enemy, classname, STR_PLAYER);
- if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
- {
- // CA and ca players when spectating enemies is forbidden
- other = CA_SpectateNext(other);
- }
- else
- {
- // other modes and ca spectators or spectating enemies is allowed
- if (!other)
- other = find(other, classname, "player");
- }
+ if (MUTATOR_CALLHOOK(SpectateNext, this, other))
+ other = M_ARGV(1, entity);
+ else if (!other)
+ other = find(other, classname, STR_PLAYER);
- if(other) { SetSpectator(self, other); }
+ if(other) { SetSpectatee(this, other); }
- return SpectateSet();
+ return SpectateSet(this);
}
- float SpectatePrev()
+ bool SpectatePrev(entity this)
{
// NOTE: chain order is from the highest to the lower entnum (unlike find)
- other = findchain(classname, "player");
+ other = findchain(classname, STR_PLAYER);
if (!other) // no player
return false;
}
}
- void LeaveSpectatorMode()
+ void LeaveSpectatorMode(entity this)
{
- if(self.caplayer)
+ if(this.caplayer)
return;
- if(nJoinAllowed(self))
+ if(nJoinAllowed(this, this))
{
- if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
+ if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
{
- self.classname = "player";
- nades_RemoveBonus(self);
+ TRANSMUTE(Player, this);
- SetSpectator(self, world);
++ SetSpectatee(self, world);
+
if(autocvar_g_campaign || autocvar_g_balance_teams)
- { JoinBestTeam(self, false, true); }
+ { JoinBestTeam(this, false, true); }
if(autocvar_g_campaign)
- { campaign_bots_may_start = 1; }
+ { campaign_bots_may_start = true; }
- Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN);
+ Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
- PutClientInServer();
+ WITHSELF(this, PutClientInServer());
- if(IS_PLAYER(self)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname); }
+ if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
}
else
- stuffcmd(self, "menu_showteamselect\n");
+ stuffcmd(this, "menu_showteamselect\n");
}
else
{