#include <common/effects/qc/globalsound.qh>
#include <common/ent_cs.qh>
#include <common/gamemodes/_mod.qh>
+#include <common/gamemodes/gamemode/lms/sv_lms.qh>
#include <common/gamemodes/gamemode/nexball/sv_nexball.qh>
#include <common/items/_mod.qh>
#include <common/items/inventory.qh>
#include <common/mutators/mutator/instagib/sv_instagib.qh>
#include <common/mutators/mutator/nades/nades.qh>
#include <common/mutators/mutator/overkill/oknex.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
#include <common/mutators/mutator/waypoints/all.qh>
#include <common/net_linked.qh>
#include <common/net_notice.qh>
FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
}
// only in right path
- if( substring(plyermodel,0,14) != "models/player/")
+ if(substring(plyermodel, 0, 14) != "models/player/")
return FallbackPlayerModel;
// only good file extensions
- if(substring(plyermodel,-4,4) != ".zym")
- if(substring(plyermodel,-4,4) != ".dpm")
- if(substring(plyermodel,-4,4) != ".iqm")
- if(substring(plyermodel,-4,4) != ".md3")
- if(substring(plyermodel,-4,4) != ".psk")
+ if(substring(plyermodel, -4, 4) != ".iqm"
+ && substring(plyermodel, -4, 4) != ".zym"
+ && substring(plyermodel, -4, 4) != ".dpm"
+ && substring(plyermodel, -4, 4) != ".md3"
+ && substring(plyermodel, -4, 4) != ".psk")
+ {
return FallbackPlayerModel;
+ }
// forbid the LOD models
- if(substring(plyermodel, -9,5) == "_lod1")
- return FallbackPlayerModel;
- if(substring(plyermodel, -9,5) == "_lod2")
+ if(substring(plyermodel, -9, 5) == "_lod1" || substring(plyermodel, -9, 5) == "_lod2")
return FallbackPlayerModel;
if(plyermodel != strtolower(plyermodel))
return FallbackPlayerModel;
if (vote_called) { VoteCount(false); }
ReadyCount();
}
+ entcs_update_players(this);
}
entity spot = SelectSpawnPoint(this, true);
this.scale = 0;
this.fade_time = 0;
this.pain_finished = 0;
- STAT(STRENGTH_FINISHED, this) = 0;
- STAT(INVINCIBLE_FINISHED, this) = 0;
- STAT(SUPERWEAPONS_FINISHED, this) = 0;
STAT(AIR_FINISHED, this) = 0;
//this.dphitcontentsmask = 0;
this.dphitcontentsmask = DPCONTENTS_SOLID;
this.punchangle = '0 0 0';
this.punchvector = '0 0 0';
this.oldvelocity = this.velocity;
- this.fire_endtime = -1;
this.event_damage = func_null;
this.event_heal = func_null;
SetPlayerTeam(this, -1, TEAM_CHANGE_SPECTATOR);
this.frags = FRAGS_SPECTATOR;
}
+
+ bot_relinkplayerlist();
+
if (CS(this).just_joined)
CS(this).just_joined = false;
}
PS(this).dual_weapons = '0 0 0';
- STAT(SUPERWEAPONS_FINISHED, this) = (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
+ if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
+ StatusEffects_apply(STATUSEFFECT_Superweapons, this, time + autocvar_g_balance_superweapons_time, 0);
this.items = start_items;
- this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
+ float shieldtime = time + autocvar_g_spawnshieldtime;
+
this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
if (!sv_ready_restart_after_countdown && time < game_starttime)
{
float f = game_starttime - time;
- this.spawnshieldtime += f;
+ shieldtime += f;
this.pauserotarmor_finished += f;
this.pauserothealth_finished += f;
this.pauseregen_finished += f;
}
+ StatusEffects_apply(STATUSEFFECT_SpawnShield, this, shieldtime, 0);
+
this.damageforcescale = autocvar_g_player_damageforcescale;
this.death_time = 0;
this.respawn_flags = 0;
this.punchangle = '0 0 0';
this.punchvector = '0 0 0';
- STAT(STRENGTH_FINISHED, this) = 0;
- STAT(INVINCIBLE_FINISHED, this) = 0;
- this.fire_endtime = -1;
STAT(REVIVE_PROGRESS, this) = 0;
this.revival_time = 0;
- // TODO: we can't set these in the PlayerSpawn hook since the target code is called before it!
- STAT(BUFFS, this) = 0;
- STAT(BUFF_TIME, this) = 0;
-
STAT(AIR_FINISHED, this) = 0;
this.waterlevel = WATERLEVEL_NONE;
this.watertype = CONTENT_EMPTY;
}
});
+ Unfreeze(this, false);
+
+ MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
+
{
string s = spot.target;
if(g_assault || g_race) // TODO: make targeting work in assault & race without this hack
spot.target = s;
}
- Unfreeze(this, false);
-
- MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
-
if (autocvar_spawn_debug)
{
sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n"));
for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
.entity weaponentity = weaponentities[slot];
+ entity w_ent = this.(weaponentity);
if(slot == 0 || autocvar_g_weaponswitch_debug == 1)
- this.(weaponentity).m_switchweapon = w_getbestweapon(this, weaponentity);
+ w_ent.m_switchweapon = w_getbestweapon(this, weaponentity);
else
- this.(weaponentity).m_switchweapon = WEP_Null;
- this.(weaponentity).m_weapon = WEP_Null;
- this.(weaponentity).weaponname = "";
- this.(weaponentity).m_switchingweapon = WEP_Null;
- this.(weaponentity).cnt = -1;
+ w_ent.m_switchweapon = WEP_Null;
+ w_ent.m_weapon = WEP_Null;
+ w_ent.weaponname = "";
+ w_ent.m_switchingweapon = WEP_Null;
+ w_ent.cnt = -1;
}
MUTATOR_CALLHOOK(PlayerWeaponSelect, this);
} else if (IS_PLAYER(this)) {
PutPlayerInServer(this);
}
+
+ bot_relinkplayerlist();
}
// TODO do we need all these fields, or should we stop autodetecting runtime
modifications = strcat(modifications, ", Weapons stay");
if(autocvar_g_jetpack)
modifications = strcat(modifications, ", Jet pack");
- if(autocvar_g_powerups == 0)
- modifications = strcat(modifications, ", No powerups");
- if(autocvar_g_powerups > 0)
- modifications = strcat(modifications, ", Powerups");
modifications = substring(modifications, 2, strlen(modifications) - 2);
string versionmessage = GetClientVersionMessage(this);
- string s = strcat(versionmessage, "^8\n^8\nhost is ^9", autocvar_hostname, "^8\n");
+ string s = strcat(versionmessage, "^8\n^8\nserver is ^9", autocvar_hostname, "^8\n");
s = strcat(s, "^8\nmatch type is ^1", gamemode_name, "^8\n");
return s;
}
-bool autocvar_sv_qcphysics = true; // TODO this is for testing - remove when qcphysics work
-
/**
=============
ClientConnect
=============
*/
.entity chatbubbleentity;
+void player_powerups_remove_all(entity this);
+
void ClientDisconnect(entity this)
{
assert(IS_CLIENT(this), return);
ReadyCount();
if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
+ player_powerups_remove_all(this); // stop powerup sound
+
ONREMOVE(this);
}
sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
}
+void player_powerups_remove_all(entity this)
+{
+ if (this.items & IT_SUPERWEAPON)
+ {
+ // don't play the poweroff sound when the game restarts or the player disconnects
+ if (time > game_starttime + 1 && IS_CLIENT(this))
+ sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
+ stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+ this.items -= (this.items & IT_SUPERWEAPON);
+ }
+}
+
void player_powerups(entity this)
{
if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !game_stopped)
else
this.modelflags &= ~MF_ROCKET;
- this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
+ this.effects &= ~EF_NODEPTHTEST;
if (IS_DEAD(this))
- {
- if (this.items & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON))
- {
- sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
- stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
- this.items &= ~ITEM_Strength.m_itemid;
- this.items &= ~ITEM_Shield.m_itemid;
- this.items -= (this.items & IT_SUPERWEAPON);
- }
- }
+ player_powerups_remove_all(this);
if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
return;
// add a way to see what the items were BEFORE all of these checks for the mutator hook
int items_prev = this.items;
- Fire_ApplyDamage(this);
- Fire_ApplyEffect(this);
-
if (!MUTATOR_IS_ENABLED(mutator_instagib))
{
- if (this.items & ITEM_Strength.m_itemid)
- {
- play_countdown(this, STAT(STRENGTH_FINISHED, this), SND_POWEROFF);
- this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
- if (time > STAT(STRENGTH_FINISHED, this))
- {
- this.items = this.items - (this.items & ITEM_Strength.m_itemid);
- //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
- Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
- }
- }
- else
- {
- if (time < STAT(STRENGTH_FINISHED, this))
- {
- this.items = this.items | ITEM_Strength.m_itemid;
- if(!g_cts)
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
- Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH);
- }
- }
- if (this.items & ITEM_Shield.m_itemid)
- {
- play_countdown(this, STAT(INVINCIBLE_FINISHED, this), SND_POWEROFF);
- this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
- if (time > STAT(INVINCIBLE_FINISHED, this))
- {
- this.items = this.items - (this.items & ITEM_Shield.m_itemid);
- //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
- Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
- }
- }
- else
- {
- if (time < STAT(INVINCIBLE_FINISHED, this))
- {
- this.items = this.items | ITEM_Shield.m_itemid;
- if(!g_cts)
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
- Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD);
- }
- }
+ // NOTE: superweapons are a special case and as such are handled here instead of the status effects system
if (this.items & IT_SUPERWEAPON)
{
if (!(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS))
{
- STAT(SUPERWEAPONS_FINISHED, this) = 0;
+ StatusEffects_remove(STATUSEFFECT_Superweapons, this, STATUSEFFECT_REMOVE_NORMAL);
this.items = this.items - (this.items & IT_SUPERWEAPON);
//Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
}
else
{
- play_countdown(this, STAT(SUPERWEAPONS_FINISHED, this), SND_POWEROFF);
- if (time > STAT(SUPERWEAPONS_FINISHED, this))
+ play_countdown(this, StatusEffects_gettime(STATUSEFFECT_Superweapons, this), SND_POWEROFF);
+ if (time > StatusEffects_gettime(STATUSEFFECT_Superweapons, this))
{
this.items = this.items - (this.items & IT_SUPERWEAPON);
STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
}
else if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
{
- if (time < STAT(SUPERWEAPONS_FINISHED, this) || (this.items & IT_UNLIMITED_SUPERWEAPONS))
+ if (time < StatusEffects_gettime(STATUSEFFECT_Superweapons, this) || (this.items & IT_UNLIMITED_SUPERWEAPONS))
{
this.items = this.items | IT_SUPERWEAPON;
if(!(this.items & IT_UNLIMITED_SUPERWEAPONS))
}
else
{
- STAT(SUPERWEAPONS_FINISHED, this) = 0;
+ if(StatusEffects_active(STATUSEFFECT_Superweapons, this))
+ StatusEffects_remove(STATUSEFFECT_Superweapons, this, STATUSEFFECT_REMOVE_TIMEOUT);
STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS;
}
}
- else
+ else if(StatusEffects_active(STATUSEFFECT_Superweapons, this)) // cheaper to check than to update each frame!
{
- STAT(SUPERWEAPONS_FINISHED, this) = 0;
+ StatusEffects_remove(STATUSEFFECT_Superweapons, this, STATUSEFFECT_REMOVE_CLEAR);
}
}
if(autocvar_g_fullbrightplayers)
this.effects = this.effects | EF_FULLBRIGHT;
- if (time >= game_starttime)
- if (time < this.spawnshieldtime)
- this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
-
MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
}
void GetPressedKeys(entity this)
{
MUTATOR_CALLHOOK(GetPressedKeys, this);
+ if (game_stopped)
+ {
+ CS(this).pressedkeys = 0;
+ STAT(PRESSED_KEYS, this) = 0;
+ return;
+ }
+
+ // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
int keys = STAT(PRESSED_KEYS, this);
keys = BITSET(keys, KEY_FORWARD, CS(this).movement.x > 0);
keys = BITSET(keys, KEY_BACKWARD, CS(this).movement.x < 0);
this.items = spectatee.items;
STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee);
STAT(HIT_TIME, this) = STAT(HIT_TIME, spectatee);
- STAT(STRENGTH_FINISHED, this) = STAT(STRENGTH_FINISHED, spectatee);
- STAT(INVINCIBLE_FINISHED, this) = STAT(INVINCIBLE_FINISHED, spectatee);
- STAT(SUPERWEAPONS_FINISHED, this) = STAT(SUPERWEAPONS_FINISHED, spectatee);
STAT(AIR_FINISHED, this) = STAT(AIR_FINISHED, spectatee);
STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
STAT(WEAPONS, this) = STAT(WEAPONS, spectatee);
return free_slots;
}
-/**
- * Checks whether the client is an observer or spectator, if so, he will get kicked after
- * g_maxplayers_spectator_blocktime seconds
- */
-void checkSpectatorBlock(entity this)
-{
- if(IS_SPEC(this) || IS_OBSERVER(this))
- if(!this.caplayer)
- if(IS_REAL_CLIENT(this))
- {
- if( time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
- Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
- dropclient(this);
- }
- }
-}
-
void PrintWelcomeMessage(entity this)
{
if(CS(this).motd_actived_time == 0)
}
.bool would_spectate;
+// merged SpectatorThink and ObserverThink (old names are here so you can grep for them)
void ObserverOrSpectatorThink(entity this)
{
bool is_spec = IS_SPEC(this);
TRANSMUTE(Observer, this);
PutClientInServer(this);
}
+ else
+ this.would_spectate = false; // unable to spectate anyone
if (is_spec)
CS(this).impulse = 0;
} else if (is_spec) {
}
}
else {
- int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? CS_CVAR(this).cvar_cl_clippedspectating : !CS_CVAR(this).cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+ bool wouldclip = CS_CVAR(this).cvar_cl_clippedspectating;
+ if (PHYS_INPUT_BUTTON_USE(this))
+ wouldclip = !wouldclip;
+ int preferred_movetype = (wouldclip ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
set_movetype(this, preferred_movetype);
}
- } else {
+ } else { // jump pressed
if ((is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)))
|| (!is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this)))) {
this.flags |= FL_JUMPRELEASED;
if (frametime) {
// physics frames: update anticheat stuff
anticheat_prethink(this);
- }
- if (blockSpectators && frametime) {
// WORKAROUND: only use dropclient in server frames (frametime set).
// Never use it in cl_movement frames (frametime zero).
- checkSpectatorBlock(this);
+ if (blockSpectators && IS_REAL_CLIENT(this)
+ && (IS_SPEC(this) || IS_OBSERVER(this)) && !this.caplayer
+ && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime))
+ {
+ if (dropclient_schedule(this))
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
+ }
}
zoomstate_set = false;
this.max_armorvalue = 0;
}
- if (frametime && IS_PLAYER(this))
+ if (frametime && IS_PLAYER(this) && time >= game_starttime)
{
if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
{
}
target_voicescript_next(this);
-
- // 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
- for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
- {
- .entity weaponentity = weaponentities[slot];
- if(this.(weaponentity).m_weapon == WEP_Null)
- this.(weaponentity).clip_load = this.(weaponentity).clip_size = 0;
- }
}
void DrownPlayer(entity this)
{
Player_Physics(this);
- if (autocvar_sv_maxidle > 0)
+ if (autocvar_sv_maxidle > 0 || (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0))
if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
if (IS_REAL_CLIENT(this))
- if (IS_PLAYER(this) || autocvar_sv_maxidle_spectatorsareidle)
+ if (IS_PLAYER(this) || autocvar_sv_maxidle_alsokickspectators)
+ if (!intermission_running) // NextLevel() kills all centerprints after setting this true
{
int totalClients = 0;
- if(autocvar_sv_maxidle_slots > 0)
+ if(autocvar_sv_maxidle > 0 && autocvar_sv_maxidle_slots > 0)
{
FOREACH_CLIENT(IS_REAL_CLIENT(it) || autocvar_sv_maxidle_slots_countbots,
{
});
}
- if (autocvar_sv_maxidle_slots > 0 && (maxclients - totalClients) > autocvar_sv_maxidle_slots)
+ if (autocvar_sv_maxidle > 0 && autocvar_sv_maxidle_slots > 0 && (maxclients - totalClients) > autocvar_sv_maxidle_slots)
{ /* do nothing */ }
else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
{
}
else
{
- float timeleft = ceil(autocvar_sv_maxidle - (time - CS(this).parm_idlesince));
- if (timeleft == min(10, autocvar_sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10
- if (!CS(this).idlekick_lasttimeleft)
+ float maxidle_time = autocvar_sv_maxidle;
+ if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+ maxidle_time = autocvar_sv_maxidle_playertospectator;
+ float timeleft = ceil(maxidle_time - (time - CS(this).parm_idlesince));
+ float countdown_time = max(min(10, maxidle_time - 1), ceil(maxidle_time * 0.33)); // - 1 to support maxidle_time <= 10
+ if (timeleft == countdown_time && !CS(this).idlekick_lasttimeleft)
+ {
+ if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOVETOSPEC_IDLING, timeleft);
+ else
Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
}
if (timeleft <= 0) {
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
- dropclient(this);
+ if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+ {
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MOVETOSPEC_IDLING, this.netname, maxidle_time);
+ if (this.caplayer)
+ this.caplayer = 0;
+ this.lms_spectate_warning = 2; // TODO: mutator hook for players forcibly moved to spectator?
+ PutObserverInServer(this);
+ }
+ else
+ {
+ if (dropclient_schedule(this))
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname, maxidle_time);
+ }
return;
}
- else if (timeleft <= 10) {
+ else if (timeleft <= countdown_time) {
if (timeleft != CS(this).idlekick_lasttimeleft)
- Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft));
+ play2(this, SND(TALK2));
CS(this).idlekick_lasttimeleft = timeleft;
}
}
DrownPlayer(this);
UpdateChatBubble(this);
if (CS(this).impulse) ImpulseCommands(this);
+ GetPressedKeys(this);
if (game_stopped)
{
CSQCMODEL_AUTOUPDATE(this);
return;
}
- GetPressedKeys(this);
}
else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
{