X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fcl_client.qc;h=32ec6eb03c9ec32963287fa2075975639c9f307a;hb=7d4d4e54a5b0fdcce80d396fd9ab8b327ae1aa73;hp=26d089245f4822a15966f7505dc68bc3a54ccf74;hpb=a0feb46a5f3f7db30054e67266e1c59f712f3da7;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 26d089245..32ec6eb03 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -23,14 +23,17 @@ #include "bot/navigation.qh" #include "../common/ent_cs.qh" -#include "../common/vehicles/all.qh" +#include "../common/state.qh" + #include "../common/triggers/teleporters.qh" +#include "../common/vehicles/all.qh" + #include "weapons/hitplot.qh" #include "weapons/weaponsystem.qh" #include "../common/net_notice.qh" -#include "../common/physics.qh" +#include "../common/physics/player.qh" #include "../common/items/all.qc" @@ -55,56 +58,44 @@ void send_CSQC_teamnagger() { 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 + 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 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); } - return true; } -void ClientData_Attach() -{SELFPARAM(); +void ClientData_Attach(entity this) +{ Net_LinkEntity(this.clientdata = new(clientdata), false, 0, ClientData_Send); make_pure(this.clientdata); self.clientdata.drawonlytoclient = this; self.clientdata.owner = this; } -void ClientData_Detach() -{SELFPARAM(); - remove(self.clientdata); - self.clientdata = world; +void ClientData_Detach(entity this) +{ + remove(this.clientdata); + self.clientdata = NULL; } void ClientData_Touch(entity e) @@ -186,7 +177,9 @@ putting a client as observer in the server */ void FixPlayermodel(entity player); void PutObserverInServer() -{SELFPARAM(); +{ + SELFPARAM(); + PlayerState_detach(this); entity spot; self.hud = HUD_NORMAL; @@ -299,9 +292,9 @@ void PutObserverInServer() 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; + PS(self).m_weapon = WEP_Null; self.weaponname = ""; - self.switchingweapon = 0; + PS(self).m_switchingweapon = WEP_Null; self.weaponmodel = ""; for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -438,6 +431,7 @@ void PutClientInServer() if (IS_OBSERVER(this)) { PutObserverInServer(); } else if (IS_PLAYER(this)) { + PlayerState_attach(this); accuracy_resend(this); if (this.team < 0) @@ -570,8 +564,6 @@ void PutClientInServer() this.bot_attack = true; this.monster_attack = true; - this.spider_slowness = 0; - this.BUTTON_ATCK = this.BUTTON_JUMP = this.BUTTON_ATCK2 = false; if (this.killcount == FRAGS_SPECTATOR) { @@ -619,11 +611,11 @@ void PutClientInServer() remove(spot); // usefull for checking if there are spawnpoints, that let drop through the floor } - this.switchweapon = w_getbestweapon(this); + PS(this).m_switchweapon = w_getbestweapon(this); this.cnt = -1; // W_LastWeapon will not complain - this.weapon = 0; + PS(this).m_weapon = WEP_Null; this.weaponname = ""; - this.switchingweapon = 0; + PS(this).m_switchingweapon = WEP_Null; if (!warmup_stage && !this.alivetime) this.alivetime = time; @@ -632,6 +624,8 @@ void PutClientInServer() } } +void ClientInit_misc(); + .float ebouncefactor, ebouncestop; // electro's values // TODO do we need all these fields, or should we stop autodetecting runtime // changes and just have a console command to update this? @@ -640,7 +634,15 @@ bool ClientInit_SendEntity(entity this, entity to, int sf) WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT); return = true; msg_entity = to; + // MSG_INIT replacement + // TODO: make easier to use Registry_send_all(); + W_PROP_reload(MSG_ONE, to); + ClientInit_misc(); + MUTATOR_CALLHOOK(Ent_Init); +} +void ClientInit_misc() +{ int channel = MSG_ONE; WriteHeader(channel, ENT_CLIENT_INIT); WriteByte(channel, g_nexball_meter_period * 32); @@ -658,19 +660,8 @@ bool ClientInit_SendEntity(entity this, entity to, int sf) else WriteString(channel, ""); WriteByte(channel, self.count * 255.0); // g_balance_armor_blockpercent - WriteCoord(channel, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO - WriteCoord(channel, self.bouncestop); // g_balance_mortar_bouncestop - WriteCoord(channel, self.ebouncefactor); // g_balance_mortar_bouncefactor - WriteCoord(channel, self.ebouncestop); // g_balance_mortar_bouncestop - WriteByte(channel, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO - WriteByte(channel, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO WriteByte(channel, serverflags); // client has to know if it should zoom or not - WriteByte(channel, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO - WriteByte(channel, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO WriteCoord(channel, autocvar_g_trueaim_minrange); - WriteByte(channel, WEP_CVAR(porto, secondary)); // WEAPONTODO - - MUTATOR_CALLHOOK(Ent_Init); } void ClientInit_CheckUpdate() @@ -681,26 +672,6 @@ void ClientInit_CheckUpdate() self.count = autocvar_g_balance_armor_blockpercent; self.SendFlags |= 1; } - if(self.bouncefactor != autocvar_g_balance_mortar_bouncefactor) // WEAPONTODO - { - self.bouncefactor = autocvar_g_balance_mortar_bouncefactor; - self.SendFlags |= 1; - } - if(self.bouncestop != autocvar_g_balance_mortar_bouncestop) - { - self.bouncestop = autocvar_g_balance_mortar_bouncestop; - self.SendFlags |= 1; - } - if(self.ebouncefactor != autocvar_g_balance_electro_secondary_bouncefactor) - { - self.ebouncefactor = autocvar_g_balance_electro_secondary_bouncefactor; - self.SendFlags |= 1; - } - if(self.ebouncestop != autocvar_g_balance_electro_secondary_bouncestop) - { - self.ebouncestop = autocvar_g_balance_electro_secondary_bouncestop; - self.SendFlags |= 1; - } } void ClientInit_Spawn() @@ -745,15 +716,15 @@ void SetChangeParms () DecodeLevelParms ============= */ -void DecodeLevelParms () -{SELFPARAM(); +void DecodeLevelParms(entity this) +{ // load parms - self.parm_idlesince = parm1; - if(self.parm_idlesince == -(86400 * 366)) - self.parm_idlesince = time; + this.parm_idlesince = parm1; + if (this.parm_idlesince == -(86400 * 366)) + this.parm_idlesince = time; // whatever happens, allow 60 seconds of idling directly after connect for map loading - self.parm_idlesince = max(self.parm_idlesince, time - sv_maxidle + 60); + this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60); MUTATOR_CALLHOOK(DecodeLevelParms); } @@ -965,6 +936,8 @@ void FixClientCvars(entity e) stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n"); if(autocvar_sv_gentle) stuffcmd(e, "cl_cmd settemp cl_gentle 1\n"); + + MUTATOR_CALLHOOK(FixClientCvars, e); } float PlayerInIDList(entity p, string idlist) @@ -1002,229 +975,195 @@ void ClientPreConnect () { GameLogEcho(sprintf(":connect:%d:%d:%s", self.playerid, - num_for_edict(self), + etof(self), ((IS_REAL_CLIENT(self)) ? self.netaddress : "bot") )); } } #endif -/* +/** ============= ClientConnect Called when a client connects to the server ============= */ -void DecodeLevelParms (); -void ClientConnect () -{SELFPARAM(); - float t; - - if(IS_CLIENT(self)) - { - LOG_INFO("Warning: ClientConnect, but already connected!\n"); - return; - } - - if(Ban_MaybeEnforceBanOnce(self)) - return; - - DecodeLevelParms(); +void ClientConnect() +{ + SELFPARAM(); + if (Ban_MaybeEnforceBanOnce(this)) return; + assert(!IS_CLIENT(this), return); + assert(player_count >= 0, player_count = 0); + this.classname = "player_joining"; + this.flags = FL_CLIENT; #ifdef WATERMARK - Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_WATERMARK, WATERMARK); + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK); #endif + this.version_nagtime = time + 10 + random() * 10; - self.classname = "player_joining"; - - self.flags = FL_CLIENT; - self.version_nagtime = time + 10 + random() * 10; - - if(player_count<0) - { - LOG_TRACE("BUG player count is lower than zero, this cannot happen!\n"); - player_count = 0; - } - - if(IS_REAL_CLIENT(self)) { PlayerStats_PlayerBasic_CheckUpdate(self); } - - PlayerScore_Attach(self); - ClientData_Attach(); - accuracy_init(self); - Inventory_new(self); + // TODO: xonstat elo.txt support, until then just 404s + if (false && IS_REAL_CLIENT(this)) { PlayerStats_PlayerBasic_CheckUpdate(this); } - bot_clientconnect(); + ClientState_attach(this); + // TODO: fold all of these into ClientState + DecodeLevelParms(this); + PlayerScore_Attach(this); + ClientData_Attach(this); + accuracy_init(this); + Inventory_new(this); + playerdemo_init(this); + anticheat_init(this); + entcs_attach(this); + W_HitPlotOpen(this); - playerdemo_init(); - - anticheat_init(); + bot_clientconnect(this); // identify the right forced team - if(autocvar_g_campaign) + if (autocvar_g_campaign) { - if(IS_REAL_CLIENT(self)) // only players, not bots + if (IS_REAL_CLIENT(this)) // only players, not bots { - switch(autocvar_g_campaign_forceteam) + switch (autocvar_g_campaign_forceteam) { - case 1: self.team_forced = NUM_TEAM_1; break; - case 2: self.team_forced = NUM_TEAM_2; break; - case 3: self.team_forced = NUM_TEAM_3; break; - case 4: self.team_forced = NUM_TEAM_4; break; - default: self.team_forced = 0; + case 1: this.team_forced = NUM_TEAM_1; break; + case 2: this.team_forced = NUM_TEAM_2; break; + case 3: this.team_forced = NUM_TEAM_3; break; + case 4: this.team_forced = NUM_TEAM_4; break; + default: this.team_forced = 0; } } } - else if(PlayerInIDList(self, autocvar_g_forced_team_red)) - self.team_forced = NUM_TEAM_1; - else if(PlayerInIDList(self, autocvar_g_forced_team_blue)) - self.team_forced = NUM_TEAM_2; - else if(PlayerInIDList(self, autocvar_g_forced_team_yellow)) - self.team_forced = NUM_TEAM_3; - else if(PlayerInIDList(self, autocvar_g_forced_team_pink)) - self.team_forced = NUM_TEAM_4; - else if(autocvar_g_forced_team_otherwise == "red") - self.team_forced = NUM_TEAM_1; - else if(autocvar_g_forced_team_otherwise == "blue") - self.team_forced = NUM_TEAM_2; - else if(autocvar_g_forced_team_otherwise == "yellow") - self.team_forced = NUM_TEAM_3; - else if(autocvar_g_forced_team_otherwise == "pink") - self.team_forced = NUM_TEAM_4; - else if(autocvar_g_forced_team_otherwise == "spectate") - self.team_forced = -1; - else if(autocvar_g_forced_team_otherwise == "spectator") - self.team_forced = -1; - else - self.team_forced = 0; - - if(!teamplay) - if(self.team_forced > 0) - self.team_forced = 0; + else if (PlayerInIDList(this, autocvar_g_forced_team_red)) this.team_forced = NUM_TEAM_1; + else if (PlayerInIDList(this, autocvar_g_forced_team_blue)) this.team_forced = NUM_TEAM_2; + else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3; + else if (PlayerInIDList(this, autocvar_g_forced_team_pink)) this.team_forced = NUM_TEAM_4; + else switch (autocvar_g_forced_team_otherwise) + { + default: this.team_forced = 0; break; + case "red": this.team_forced = NUM_TEAM_1; break; + case "blue": this.team_forced = NUM_TEAM_2; break; + case "yellow": this.team_forced = NUM_TEAM_3; break; + case "pink": this.team_forced = NUM_TEAM_4; break; + case "spectate": + case "spectator": + this.team_forced = -1; + break; + } + if (!teamplay && this.team_forced > 0) this.team_forced = 0; - JoinBestTeam(self, false, false); // if the team number is valid, keep it + JoinBestTeam(this, false, false); // if the team number is valid, keep it - if((autocvar_sv_spectate == 1) || autocvar_g_campaign || self.team_forced < 0) { - self.classname = STR_OBSERVER; + if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) { + this.classname = STR_OBSERVER; } else { - if(teamplay) + if (!teamplay || autocvar_g_balance_teams) { - if(autocvar_g_balance_teams) - { - self.classname = STR_PLAYER; - campaign_bots_may_start = 1; - } - else - { - self.classname = STR_OBSERVER; // do it anyway - } + this.classname = STR_PLAYER; + campaign_bots_may_start = 1; } else { - self.classname = STR_PLAYER; - campaign_bots_may_start = 1; + this.classname = STR_OBSERVER; // do it anyway } } - self.playerid = (playerid_last = playerid_last + 1); - - PlayerStats_GameReport_AddEvent(sprintf("kills-%d", self.playerid)); + this.playerid = ++playerid_last; - if(IS_BOT_CLIENT(self)) - PlayerStats_GameReport_AddPlayer(self); + PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid)); - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":join:", ftos(self.playerid), ":", ftos(num_for_edict(self)), ":", ((IS_REAL_CLIENT(self)) ? self.netaddress : "bot"), ":", self.netname)); + // always track bots, don't ask for cl_allow_uidtracking + if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this); - LogTeamchange(self.playerid, self.team, 1); + if (autocvar_sv_eventlog) + GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname)); - self.just_joined = true; // stop spamming the eventlog with additional lines when the client connects + LogTeamchange(this.playerid, this.team, 1); - self.netname_previous = strzone(self.netname); + this.just_joined = true; // stop spamming the eventlog with additional lines when the client connects - if(IS_PLAYER(self) && teamplay) - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(self, INFO_JOIN_CONNECT_TEAM_), self.netname); - else - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_CONNECT, self.netname); + this.netname_previous = strzone(this.netname); - stuffcmd(self, strcat(clientstuff, "\n")); - stuffcmd(self, "cl_particles_reloadeffects\n"); // TODO do we still need this? + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, (teamplay ? APP_TEAM_ENT_4(this, INFO_JOIN_CONNECT_TEAM_) : INFO_JOIN_CONNECT), this.netname); - FixClientCvars(self); + stuffcmd(this, clientstuff, "\n"); + stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this? - // spawnfunc_waypoint sprites - WaypointSprite_InitClient(self); + FixClientCvars(this); - // Wazat's grappling hook - SetGrappleHookBindings(); + // Grappling hook + stuffcmd(this, "alias +hook +button6\n"); + stuffcmd(this, "alias -hook -button6\n"); // Jetpack binds - stuffcmd(self, "alias +jetpack +button10\n"); - stuffcmd(self, "alias -jetpack -button10\n"); + stuffcmd(this, "alias +jetpack +button10\n"); + stuffcmd(this, "alias -jetpack -button10\n"); // get version info from player - stuffcmd(self, "cmd clientversion $gameversion\n"); + stuffcmd(this, "cmd clientversion $gameversion\n"); // get other cvars from player GetCvars(0); // notify about available teams - if(teamplay) + if (teamplay) { - CheckAllowedTeams(self); - t = 0; if(c1 >= 0) t |= 1; if(c2 >= 0) t |= 2; if(c3 >= 0) t |= 4; if(c4 >= 0) t |= 8; - stuffcmd(self, strcat("set _teams_available ", ftos(t), "\n")); + CheckAllowedTeams(this); + int t = 0; + if (c1 >= 0) t |= BIT(0); + if (c2 >= 0) t |= BIT(1); + if (c3 >= 0) t |= BIT(2); + if (c4 >= 0) t |= BIT(3); + stuffcmd(this, sprintf("set _teams_available %d\n", t)); } else - stuffcmd(self, "set _teams_available 0\n"); - - entcs_attach(self); + { + stuffcmd(this, "set _teams_available 0\n"); + } bot_relinkplayerlist(); - self.spectatortime = time; - if(blockSpectators) + this.spectatortime = time; + if (blockSpectators) { - Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); } - self.jointime = time; - self.allowed_timeouts = autocvar_sv_timeout_number; + this.jointime = time; + this.allowed_timeouts = autocvar_sv_timeout_number; - if(IS_REAL_CLIENT(self)) + if (IS_REAL_CLIENT(this)) { - if(!autocvar_g_campaign) + if (!autocvar_g_campaign) { - self.motd_actived_time = -1; - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage()); + this.motd_actived_time = -1; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage()); } - if(autocvar_g_bugrigs || (g_weaponarena_weapons == WEPSET(TUBA))) - stuffcmd(self, "cl_cmd settemp chase_active 1\n"); + if (g_weaponarena_weapons == WEPSET(TUBA)) + stuffcmd(this, "cl_cmd settemp chase_active 1\n"); } - if(!sv_foginterval && world.fog != "") - stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); - - W_HitPlotOpen(self); + if (!sv_foginterval && world.fog != "") + stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); - if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts - send_CSQC_teamnagger(); + if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1))) + if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts + send_CSQC_teamnagger(); - CheatInitClient(); + CSQCMODEL_AUTOINIT(this); - CSQCMODEL_AUTOINIT(self); + this.model_randomizer = random(); - self.model_randomizer = random(); + if (IS_REAL_CLIENT(this)) + sv_notice_join(this); - if(IS_REAL_CLIENT(self)) - sv_notice_join(); - - for (entity e = world; (e = findfloat(e, init_for_player_needed, 1)); ) { - WITH(entity, self, e, e.init_for_player(this)); - } + FOREACH_ENTITY_FLOAT(init_for_player_needed, true, { + WITH(entity, self, it, it.init_for_player(it)); + }); - MUTATOR_CALLHOOK(ClientConnect, self); + MUTATOR_CALLHOOK(ClientConnect, this); } /* ============= @@ -1236,7 +1175,9 @@ Called when a client disconnects from the server .entity chatbubbleentity; void ReadyCount(); void ClientDisconnect () -{SELFPARAM(); +{ + SELFPARAM(); + ClientState_detach(this); if(self.vehicle) vehicles_exit(VHEF_RELEASE); @@ -1295,7 +1236,7 @@ void ClientDisconnect () accuracy_free(self); Inventory_delete(self); - ClientData_Detach(); + ClientData_Detach(this); PlayerScore_Detach(self); if(self.netname_previous) @@ -1617,7 +1558,8 @@ void player_regen () { if(self.vehicle) vehicles_exit(VHEF_RELEASE); - self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0'); + if(self.event_damage) + self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0'); } if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) @@ -1666,8 +1608,8 @@ spectate mode routines ====================== */ -void SpectateCopy(entity spectatee) -{SELFPARAM(); +void SpectateCopy(entity this, entity spectatee) +{ MUTATOR_CALLHOOK(SpectateCopy, spectatee, self); self.armortype = spectatee.armortype; self.armorvalue = spectatee.armorvalue; @@ -1689,9 +1631,9 @@ void SpectateCopy(entity spectatee) self.invincible_finished = spectatee.invincible_finished; self.pressedkeys = spectatee.pressedkeys; self.weapons = spectatee.weapons; - self.switchweapon = spectatee.switchweapon; - self.switchingweapon = spectatee.switchingweapon; - self.weapon = spectatee.weapon; + PS(self).m_switchweapon = PS(spectatee).m_switchweapon; + PS(self).m_switchingweapon = PS(spectatee).m_switchingweapon; + PS(self).m_weapon = PS(spectatee).m_weapon; self.vortex_charge = spectatee.vortex_charge; self.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo; self.hagar_load = spectatee.hagar_load; @@ -1752,7 +1694,7 @@ bool SpectateUpdate() return false; } - SpectateCopy(self.enemy); + SpectateCopy(this, this.enemy); return true; } @@ -1876,7 +1818,7 @@ void LeaveSpectatorMode() {SELFPARAM(); if(self.caplayer) return; - if(nJoinAllowed(self)) + if(nJoinAllowed(self, self)) { if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0) { @@ -1910,40 +1852,39 @@ void LeaveSpectatorMode() * it checks whether the number of currently playing players exceeds g_maxplayers. * @return int number of free slots for players, 0 if none */ -float nJoinAllowed(entity ignore) -{SELFPARAM(); +bool nJoinAllowed(entity this, entity ignore) +{ if(!ignore) // this is called that way when checking if anyone may be able to join (to build qcstatus) // so report 0 free slots if restricted { if(autocvar_g_forced_team_otherwise == "spectate") - return 0; + return false; if(autocvar_g_forced_team_otherwise == "spectator") - return 0; + return false; } - if(self.team_forced < 0) - return 0; // forced spectators can never join + if(this.team_forced < 0) + return false; // forced spectators can never join // TODO simplify this - entity e; - float totalClients = 0; - FOR_EACH_CLIENT(e) - if(e != ignore) - totalClients += 1; + int totalClients = 0; + int currentlyPlaying = 0; + FOREACH_CLIENT(true, LAMBDA( + if(it != ignore) + ++totalClients; + if(IS_REAL_CLIENT(it)) + if(IS_PLAYER(it) || it.caplayer) + ++currentlyPlaying; + )); if (!autocvar_g_maxplayers) return maxclients - totalClients; - float currentlyPlaying = 0; - FOR_EACH_REALCLIENT(e) - if(IS_PLAYER(e) || e.caplayer) - currentlyPlaying += 1; - if(currentlyPlaying < autocvar_g_maxplayers) return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); - return 0; + return false; } /** @@ -2014,7 +1955,7 @@ void ObserverThink() {SELFPARAM(); if ( self.impulse ) { - MinigameImpulse(self.impulse); + MinigameImpulse(self, self.impulse); self.impulse = 0; } float prefered_movetype; @@ -2049,7 +1990,7 @@ void SpectatorThink() {SELFPARAM(); if ( self.impulse ) { - if(MinigameImpulse(self.impulse)) + if(MinigameImpulse(self, self.impulse)) self.impulse = 0; } if (self.flags & FL_JUMPRELEASED) { @@ -2324,22 +2265,6 @@ void PlayerPreThink () if(frametime) { - if(self.weapon == WEP_VORTEX.m_id && WEP_CVAR(vortex, charge)) - { - self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit)); - self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit)); - self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit)); - - if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit)) - { - self.weaponentity_glowmod_x = self.weaponentity_glowmod.x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit)); - self.weaponentity_glowmod_y = self.weaponentity_glowmod.y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit)); - self.weaponentity_glowmod_z = self.weaponentity_glowmod.z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit)); - } - } - else - self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, true) * 2; - player_powerups(); } @@ -2421,7 +2346,7 @@ void PlayerPreThink () // WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY // It cannot be predicted by the engine! .entity weaponentity = weaponentities[0]; // TODO: unhardcode - if((self.weapon == WEP_SHOCKWAVE.m_id || self.weapon == WEP_SHOTGUN.m_id) && self.(weaponentity).wframe == WFRAME_FIRE2 && time < self.(weaponentity).weapon_nextthink) + if((PS(self).m_weapon == WEP_SHOCKWAVE || PS(self).m_weapon == WEP_SHOTGUN) && self.(weaponentity).wframe == WFRAME_FIRE2 && time < self.(weaponentity).weapon_nextthink) do_crouch = 0; if (do_crouch) @@ -2429,8 +2354,8 @@ void PlayerPreThink () if (!self.crouch) { self.crouch = true; - self.view_ofs = self.stat_pl_crouch_view_ofs; - setsize (self, self.stat_pl_crouch_min, self.stat_pl_crouch_max); + self.view_ofs = STAT(PL_CROUCH_VIEW_OFS, self); + setsize (self, STAT(PL_CROUCH_MIN, self), STAT(PL_CROUCH_MAX, self)); // setanim(self, self.anim_duck, false, true, true); // this anim is BROKEN anyway } } @@ -2438,12 +2363,12 @@ void PlayerPreThink () { if (self.crouch) { - tracebox(self.origin, self.stat_pl_min, self.stat_pl_max, self.origin, false, self); + tracebox(self.origin, STAT(PL_MIN, self), STAT(PL_MAX, self), self.origin, false, self); if (!trace_startsolid) { self.crouch = false; - self.view_ofs = self.stat_pl_view_ofs; - setsize (self, self.stat_pl_min, self.stat_pl_max); + self.view_ofs = STAT(PL_VIEW_OFS, self); + setsize (self, STAT(PL_MIN, self), STAT(PL_MAX, self)); } } } @@ -2496,19 +2421,26 @@ void PlayerPreThink () // WEAPONTODO: Add weapon request for this if(!zoomstate_set) - SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_VORTEX.m_id) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE.m_id && WEP_CVAR(rifle, secondary) == 0)); // WEAPONTODO + SetZoomState( + self.BUTTON_ZOOM + || self.BUTTON_ZOOMSCRIPT + || (self.BUTTON_ATCK2 && PS(self).m_weapon == WEP_VORTEX) + || (self.BUTTON_ATCK2 && PS(self).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0) + ); // WEAPONTODO float oldspectatee_status; oldspectatee_status = self.spectatee_status; if(IS_SPEC(self)) - self.spectatee_status = num_for_edict(self.enemy); + self.spectatee_status = etof(self.enemy); else if(IS_OBSERVER(self)) - self.spectatee_status = num_for_edict(self); + self.spectatee_status = etof(self); else self.spectatee_status = 0; if(self.spectatee_status != oldspectatee_status) { ClientData_Touch(self); + if(g_race || g_cts) + race_InitSpectator(); } if(self.teamkill_soundtime) @@ -2516,31 +2448,50 @@ void PlayerPreThink () { self.teamkill_soundtime = 0; - setself(self.teamkill_soundsource); - entity oldpusher = self.pusher; - self.pusher = this; - - PlayerSound(playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY); - - self.pusher = oldpusher; - setself(this); + entity e = self.teamkill_soundsource; + entity oldpusher = e.pusher; + e.pusher = this; + PlayerSound(e, playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY); + e.pusher = oldpusher; } if(self.taunt_soundtime) if(time > self.taunt_soundtime) { self.taunt_soundtime = 0; - PlayerSound(playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT); + PlayerSound(self, playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT); } target_voicescript_next(self); // WEAPONTODO: Move into weaponsystem somehow // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring - if(!self.weapon) + if (PS(self).m_weapon == WEP_Null) self.clip_load = self.clip_size = 0; } +void DrownPlayer(entity this) +{ + if(this.deadflag != DEAD_NO) + return; + + if (this.waterlevel != WATERLEVEL_SUBMERGED) + { + if(this.air_finished < time) + PlayerSound(this, playersound_gasp, CH_PLAYER, VOICETYPE_PLAYERSOUND); + this.air_finished = time + autocvar_g_balance_contents_drowndelay; + this.dmg = 2; + } + else if (this.air_finished < time) + { // drown! + if (this.pain_finished < time) + { + Damage (this, world, world, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0'); + this.pain_finished = time + 0.5; + } + } +} + /* ============= PlayerPostThink @@ -2592,10 +2543,11 @@ void PlayerPostThink () //CheckPlayerJump(); if(IS_PLAYER(self)) { + DrownPlayer(self); CheckRules_Player(); UpdateChatBubble(); if (self.impulse) - ImpulseCommands(); + ImpulseCommands(self); if (intermission_running) return; // intermission or finale GetPressedKeys();