}
DropAllRunes(self);
+ MUTATOR_CALLHOOK(MakePlayerObserver);
Portal_ClearAll(self);
PlayerScore_Clear(self); // clear scores when needed
+ accuracy_resend(self);
+
self.spectatortime = time;
self.classname = "observer";
}
else
self.frags = FRAGS_SPECTATOR;
-
- MUTATOR_CALLHOOK(MakePlayerObserver);
}
float RestrictSkin(float s)
entity spot, oldself;
float j;
+ accuracy_resend(self);
+
if(self.team < 0)
JoinBestTeam(self, FALSE, TRUE);
self.air_finished = time + 12;
self.dmg = 2;
if(cvar("g_balance_nex_charge"))
+ {
+ if(cvar("g_balance_nex_secondary_charge_pool"))
+ self.nex_charge_pool_ammo = 1;
self.nex_charge = cvar("g_balance_nex_charge_start");
+ }
if(inWarmupStage)
{
}
if(g_weaponarena_random)
+ {
+ if(g_weaponarena_random_with_laser)
+ self.weapons &~= WEPBIT_LASER;
self.weapons = randombits(self.weapons, g_weaponarena_random, FALSE);
+ if(g_weaponarena_random_with_laser)
+ self.weapons |= WEPBIT_LASER;
+ }
self.items = start_items;
self.jump_interval = time;
// ctf_playerchanged();
}
+.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?
float ClientInit_SendEntity(entity to, float sf)
{
WriteByte(MSG_ENTITY, ENT_CLIENT_INIT);
WriteString(MSG_ENTITY, "");
WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent
WriteByte(MSG_ENTITY, self.cnt * 255.0); // g_balance_weaponswitchdelay
- WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_secondary_bouncefactor
- WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_secondary_bouncestop
+ WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_bouncefactor
+ WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_bouncestop
+ WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_grenadelauncher_bouncefactor
+ WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_grenadelauncher_bouncestop
WriteByte(MSG_ENTITY, cvar("g_balance_nex_secondary")); // client has to know if it should zoom or not
WriteByte(MSG_ENTITY, cvar("g_balance_campingrifle_secondary")); // client has to know if it should zoom or not
+ WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
return TRUE;
}
self.cnt = cvar("g_balance_weaponswitchdelay");
self.SendFlags |= 1;
}
- if(self.bouncefactor != cvar("g_balance_grenadelauncher_secondary_bouncefactor"))
+ if(self.bouncefactor != cvar("g_balance_grenadelauncher_bouncefactor"))
+ {
+ self.bouncefactor = cvar("g_balance_grenadelauncher_bouncefactor");
+ self.SendFlags |= 1;
+ }
+ if(self.bouncestop != cvar("g_balance_grenadelauncher_bouncestop"))
{
- self.bouncefactor = cvar("g_balance_grenadelauncher_secondary_bouncefactor");
+ self.bouncestop = cvar("g_balance_grenadelauncher_bouncestop");
self.SendFlags |= 1;
}
- if(self.bouncestop != cvar("g_balance_grenadelauncher_secondary_bouncestop"))
+ if(self.ebouncefactor != cvar("g_balance_electro_secondary_bouncefactor"))
{
- self.bouncestop = cvar("g_balance_grenadelauncher_secondary_bouncestop");
+ self.ebouncefactor = cvar("g_balance_electro_secondary_bouncefactor");
+ self.SendFlags |= 1;
+ }
+ if(self.ebouncestop != cvar("g_balance_electro_secondary_bouncestop"))
+ {
+ self.ebouncestop = cvar("g_balance_electro_secondary_bouncestop");
self.SendFlags |= 1;
}
}
*/
}
+float PlayerInIDList(entity p, string idlist)
+{
+ float n, i;
+ string s;
+
+ // NOTE: we do NOT check crypto_keyfp here, an unsigned ID is fine too for this
+ if not(p.crypto_idfp)
+ return 0;
+
+ // this function allows abbreviated player IDs too!
+ n = tokenize_console(idlist);
+ for(i = 0; i < n; ++i)
+ {
+ s = argv(i);
+ if(s == substring(p.crypto_idfp, 0, strlen(s)))
+ return 1;
+ }
+
+ return 0;
+}
+
/*
=============
ClientConnect
PlayerScore_Attach(self);
ClientData_Attach();
+ accuracy_init(self);
bot_clientconnect();
//if(g_domination)
// dom_player_join_team(self);
+ // identify the right forced team
+ if(PlayerInIDList(self, cvar_string("g_forced_team_red")))
+ self.team_forced = COLOR_TEAM1;
+ else if(PlayerInIDList(self, cvar_string("g_forced_team_blue")))
+ self.team_forced = COLOR_TEAM2;
+ else if(PlayerInIDList(self, cvar_string("g_forced_team_yellow")))
+ self.team_forced = COLOR_TEAM3;
+ else if(PlayerInIDList(self, cvar_string("g_forced_team_pink")))
+ self.team_forced = COLOR_TEAM4;
+ else if(cvar_string("g_forced_team_otherwise") == "red")
+ self.team_forced = COLOR_TEAM1;
+ else if(cvar_string("g_forced_team_otherwise") == "blue")
+ self.team_forced = COLOR_TEAM2;
+ else if(cvar_string("g_forced_team_otherwise") == "yellow")
+ self.team_forced = COLOR_TEAM3;
+ else if(cvar_string("g_forced_team_otherwise") == "pink")
+ self.team_forced = COLOR_TEAM4;
+ else if(cvar_string("g_forced_team_otherwise") == "spectate")
+ self.team_forced = -1;
+ else if(cvar_string("g_forced_team_otherwise") == "spectator")
+ self.team_forced = -1;
+ else
+ self.team_forced = 0;
+
+ if(!teams_matter)
+ if(self.team_forced > 0)
+ self.team_forced = 0;
+
JoinBestTeam(self, FALSE, FALSE); // if the team number is valid, keep it
- if((cvar("sv_spectate") == 1 && !g_lms) || cvar("g_campaign")) {
+ if((cvar("sv_spectate") == 1 && !g_lms) || cvar("g_campaign") || self.team_forced < 0) {
self.classname = "observer";
} else {
if(teams_matter)
bot_relinkplayerlist();
- // remove laserdot
- if(self.weaponentity)
- if(self.weaponentity.lasertarget)
- remove(self.weaponentity.lasertarget);
-
if(g_arena)
{
Spawnqueue_Unmark(self);
Spawnqueue_Remove(self);
}
+ accuracy_free(self);
ClientData_Detach();
PlayerScore_Detach(self);
self.pressedkeys &~= KEY_CROUCH;
}
-void update_stats (float number, float hit, float fired) {
-// self.stat_hit = number + ((number==0) ? 1 : 64) * hit * sv_accuracy_data_share;
-// self.stat_fired = number + ((number==0) ? 1 : 64) * fired * sv_accuracy_data_share;
-
- if(number) {
- self.stat_hit = number + 64 * hit * sv_accuracy_data_share;
- self.stat_fired = number + 64 * fired * sv_accuracy_data_share;
- } else {
- self.stat_hit = hit * sv_accuracy_data_share;
- self.stat_fired = fired * sv_accuracy_data_share;
- }
-}
-
/*
======================
spectate mode routines
======================
*/
-.float weapon_count;
void SpectateCopy(entity spectatee) {
- if(spectatee.weapon_count < WEP_LAST) {
- update_stats (spectatee.weapon_count, spectatee.cvar_cl_accuracy_data_share * floor(spectatee.stats_hit[spectatee.weapon_count - 1]), spectatee.cvar_cl_accuracy_data_share * floor(spectatee.stats_fired[spectatee.weapon_count - 1]));
- spectatee.weapon_count ++;
- } else
- update_stats (0, spectatee.cvar_cl_accuracy_data_share * spectatee.stat_hit, spectatee.cvar_cl_accuracy_data_share * spectatee.stat_fired);
-
other = spectatee;
MUTATOR_CALLHOOK(SpectateCopy);
self.armortype = spectatee.armortype;
WriteEntity(MSG_ONE, self.enemy);
//stuffcmd(self, "set viewsize $tmpviewsize \n");
self.movetype = MOVETYPE_NONE;
-
- self.enemy.weapon_count = 0;
+ accuracy_resend(self);
if(!SpectateUpdate())
PutObserverInServer();
void LeaveSpectatorMode()
{
if(isJoinAllowed()) {
- if(!teams_matter || cvar("g_campaign") || cvar("g_balance_teams") || (self.wasplayer && cvar("g_changeteam_banned"))) {
+ if(!teams_matter || cvar("g_campaign") || cvar("g_balance_teams") || (self.wasplayer && cvar("g_changeteam_banned")) || self.team_forced > 0) {
self.classname = "player";
if(cvar("g_campaign") || cvar("g_balance_teams") || cvar("g_balance_teams_force"))
if(cvar("g_campaign"))
campaign_bots_may_start = 1;
- self.stat_count = WEP_LAST;
-
PutClientInServer();
if(self.classname == "player")
* @return bool TRUE if the player is allowed to join, false otherwise
*/
float isJoinAllowed() {
+ if(self.team_forced < 0)
+ return FALSE; // forced spectators can never join
+
if (!cvar("g_maxplayers"))
return TRUE;
}
}
-float vercmp_recursive(string v1, string v2)
-{
- float dot1, dot2;
- string s1, s2;
- float r;
-
- dot1 = strstrofs(v1, ".", 0);
- dot2 = strstrofs(v2, ".", 0);
- if(dot1 == -1)
- s1 = v1;
- else
- s1 = substring(v1, 0, dot1);
- if(dot2 == -1)
- s2 = v2;
- else
- s2 = substring(v2, 0, dot2);
-
- r = stof(s1) - stof(s2);
- if(r != 0)
- return r;
-
- r = strcasecmp(s1, s2);
- if(r != 0)
- return r;
-
- if(dot1 == -1)
- if(dot2 == -1)
- return 0;
- else
- return -1;
- else
- if(dot2 == -1)
- return 1;
- else
- return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
-}
-
-float vercmp(string v1, string v2)
-{
- if(strcasecmp(v1, v2) == 0) // early out check
- return 0;
- return vercmp_recursive(v1, v2);
-}
-
void ObserverThink()
{
if (self.flags & FL_JUMPRELEASED) {
self.classname = "spectator";
} else {
self.classname = "observer";
- self.stat_count = WEP_LAST;
PutClientInServer();
}
} else if (self.BUTTON_ATCK2) {
self.welcomemessage_time = 0;
self.flags &~= FL_JUMPRELEASED;
self.classname = "observer";
- self.stat_count = WEP_LAST;
PutClientInServer();
} else {
if(!SpectateUpdate())
}
player_regen();
+
+ // rot nex charge to the charge limit
+ if(cvar("g_balance_nex_charge_rot_rate") && self.nex_charge > cvar("g_balance_nex_charge_limit") && self.nex_charge_rottime < time)
+ self.nex_charge = bound(cvar("g_balance_nex_charge_limit"), self.nex_charge - cvar("g_balance_nex_charge_rot_rate") * frametime / W_TICSPERFRAME, 1);
+
if(frametime)
player_anim();
if (g_minstagib)
minstagib_ammocheck();
- ctf_setstatus();
- nexball_setstatus();
+ if(g_ctf)
+ ctf_setstatus();
+
+ if(g_nexball)
+ nexball_setstatus();
self.dmg_team = max(0, self.dmg_team - cvar("g_teamdamage_resetspeed") * frametime);
stuffcmd(self, strcat("name ", self.netname, substring(ftos(random()), 2, -1), "\n"));
}
- // send the clients accuracy stats to the client
- if(self.stat_count > 0)
- if(frametime)
- {
- self.stat_hit = self.stat_count + 64 * floor(self.(stats_hit[self.stat_count - 1]));
- self.stat_fired = self.stat_count + 64 * floor(self.(stats_fired[self.stat_count - 1]));
- self.stat_count -= 1;
- }
-
if(sv_maxidle && frametime)
{
// WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).