#include <common/effects/qc/globalsound.qh>
+#include "../common/triggers/func/conveyor.qh"
#include "../common/triggers/teleporters.qh"
#include "../common/vehicles/all.qh"
#include "weapons/weaponsystem.qh"
#include "../common/net_notice.qh"
+#include "../common/net_linked.qh"
#include "../common/physics/player.qh"
#include "../common/items/_mod.qh"
.string netname_previous;
-void SetSpectatee(entity player, entity spectatee);
+void SetSpectatee(entity this, entity spectatee);
+void SetSpectatee_status(entity this, int spectatee_num);
/*
if (this.killcount != FRAGS_SPECTATOR)
{
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
- if(!intermission_running)
- if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
+ if(!gameover)
+ if(autocvar_g_chat_nospectators == 1 || (!warmup_stage && autocvar_g_chat_nospectators == 2))
Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
if(this.just_joined == false) {
accuracy_resend(this);
this.spectatortime = time;
+ if(this.bot_attack)
+ IL_REMOVE(g_bot_targets, this);
this.bot_attack = false;
this.hud = HUD_NORMAL;
TRANSMUTE(Observer, this);
this.iscreature = false;
this.teleportable = TELEPORT_SIMPLE;
+ if(this.damagedbycontents)
+ IL_REMOVE(g_damagedbycontents, this);
this.damagedbycontents = false;
this.health = FRAGS_SPECTATOR;
+ SetSpectatee_status(this, etof(this));
this.takedamage = DAMAGE_NO;
this.solid = SOLID_NOT;
set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink
this.hook_time = 0;
this.deadflag = DEAD_NO;
this.crouch = false;
+ this.revive_progress = 0;
this.revival_time = 0;
this.items = 0;
}
TRANSMUTE(Player, this);
+
this.wasplayer = true;
this.iscreature = true;
this.teleportable = TELEPORT_NORMAL;
+ if(!this.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, this);
this.damagedbycontents = true;
set_movetype(this, MOVETYPE_WALK);
this.solid = SOLID_SLIDEBOX;
this.armorvalue = start_armorvalue;
this.weapons = start_weapons;
}
+ SetSpectatee_status(this, 0);
this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
this.strength_finished = 0;
this.invincible_finished = 0;
this.fire_endtime = -1;
+ this.revive_progress = 0;
this.revival_time = 0;
this.air_finished = time + 12;
FixPlayermodel(this);
this.drawonlytoclient = NULL;
+ this.viewloc = NULL;
+
this.crouch = false;
- this.view_ofs = STAT(PL_VIEW_OFS, NULL);
- setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+ this.view_ofs = STAT(PL_VIEW_OFS, this);
+ setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
this.spawnorigin = spot.origin;
setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
// don't reset back to last position, even if new position is stuck in solid
this.oldorigin = this.origin;
this.prevorigin = this.origin;
this.lastteleporttime = time; // prevent insane speeds due to changing origin
+ if(this.conveyor)
+ IL_REMOVE(g_conveyed, this);
this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
this.hud = HUD_NORMAL;
this.event_damage = PlayerDamage;
+ if(!this.bot_attack)
+ IL_PUSH(g_bot_targets, this);
this.bot_attack = true;
this.monster_attack = true;
void ClientInit_misc(entity this);
-.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?
bool ClientInit_SendEntity(entity this, entity to, int sf)
else
WriteString(channel, "");
WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
- WriteByte(channel, serverflags); // client has to know if it should zoom or not
+ WriteByte(channel, serverflags);
WriteCoord(channel, autocvar_g_trueaim_minrange);
}
this.killindicator.count = bound(0, ceil(killtime), 10);
//sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n"));
- FOREACH_ENTITY_ENT(enemy, this,
+ IL_EACH(g_clones, it.enemy == this && !(it.effects & CSQCMODEL_EF_RESPAWNGHOST),
{
- if(it.classname != "body")
- continue;
it.killindicator = spawn();
it.killindicator.owner = it;
it.killindicator.scale = 0.5;
Called once (not at each match start) when a client begins a connection to the server
=============
*/
-void ClientPreConnect ()
-{ENGINE_EVENT();
+void ClientPreConnect(entity this)
+{
if(autocvar_sv_eventlog)
{
GameLogEcho(sprintf(":connect:%d:%d:%s",
this.netname_previous = strzone(this.netname);
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname);
+ if(teamplay && IS_PLAYER(this))
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname);
+ else
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname);
stuffcmd(this, clientstuff, "\n");
stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
if (IS_REAL_CLIENT(this))
{
- if (!autocvar_g_campaign)
- {
- this.motd_actived_time = -1;
- Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
- }
-
if (g_weaponarena_weapons == WEPSET(TUBA))
stuffcmd(this, "cl_cmd settemp chase_active 1\n");
}
if (IS_REAL_CLIENT(this))
sv_notice_join(this);
- FOREACH_ENTITY_FLOAT(init_for_player_needed, true, {
+ // update physics stats (players can spawn before physics runs)
+ Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
+
+ IL_EACH(g_initforplayer, it.init_for_player, {
it.init_for_player(it, this);
});
MUTATOR_CALLHOOK(ClientConnect, this);
+
+ if (IS_REAL_CLIENT(this))
+ {
+ if (!autocvar_g_campaign && !IS_PLAYER(this))
+ {
+ this.motd_actived_time = -1;
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
+ }
+ }
}
/*
=============
this.playerid = 0;
ReadyCount();
if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
+
+ ONREMOVE(this);
}
void ChatBubbleThink(entity this)
}
bool zoomstate_set;
-void SetZoomState(entity this, float z)
+void SetZoomState(entity this, float newzoom)
{
- if(z != this.zoomstate)
+ if(newzoom != this.zoomstate)
{
- this.zoomstate = z;
+ this.zoomstate = newzoom;
ClientData_Touch(this);
}
zoomstate_set = true;
void GetPressedKeys(entity this)
{
MUTATOR_CALLHOOK(GetPressedKeys, this);
- int keys = this.pressedkeys;
+ int keys = STAT(PRESSED_KEYS, this);
keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0);
keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0);
keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0);
keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this));
keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
- this.pressedkeys = keys;
+ this.pressedkeys = keys; // store for other users
+
+ STAT(PRESSED_KEYS, this) = keys;
}
/*
this.hit_time = spectatee.hit_time;
this.strength_finished = spectatee.strength_finished;
this.invincible_finished = spectatee.invincible_finished;
- this.pressedkeys = spectatee.pressedkeys;
+ STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
this.weapons = spectatee.weapons;
this.vortex_charge = spectatee.vortex_charge;
this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
this.angles = spectatee.v_angle;
STAT(FROZEN, this) = STAT(FROZEN, spectatee);
this.revive_progress = spectatee.revive_progress;
+ this.viewloc = spectatee.viewloc;
if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
this.fixangle = true;
setorigin(this, spectatee.origin);
bool SpectateUpdate(entity this)
{
if(!this.enemy)
- return false;
+ return false;
if(!IS_PLAYER(this.enemy) || this == this.enemy)
{
return true;
}
+void SetSpectatee_status(entity this, int spectatee_num)
+{
+ int oldspectatee_status = this.spectatee_status;
+ this.spectatee_status = spectatee_num;
+
+ if (this.spectatee_status != oldspectatee_status)
+ {
+ ClientData_Touch(this);
+ if (g_race || g_cts) race_InitSpectator();
+ }
+}
+
void SetSpectatee(entity this, entity spectatee)
{
entity old_spectatee = this.enemy;
}
}
+ if (this.enemy)
+ SetSpectatee_status(this, etof(this.enemy));
+
// needed to update spectator list
if(old_spectatee) { ClientData_Touch(old_spectatee); }
}
}
}
-.float caplayer;
-
-void LeaveSpectatorMode(entity this)
+.bool team_selected;
+bool ShowTeamSelection(entity this)
{
- if(this.caplayer)
- return;
- if(nJoinAllowed(this, this))
- {
- if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
- {
- TRANSMUTE(Player, this);
-
- SetSpectatee(this, NULL);
+ if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
+ return false;
+ stuffcmd(this, "menu_showteamselect\n");
+ return true;
+}
+void Join(entity this)
+{
+ TRANSMUTE(Player, this);
- if(autocvar_g_campaign || autocvar_g_balance_teams)
- { JoinBestTeam(this, false, true); }
+ if(!this.team_selected)
+ if(autocvar_g_campaign || autocvar_g_balance_teams)
+ JoinBestTeam(this, false, true);
- if(autocvar_g_campaign)
- { campaign_bots_may_start = true; }
+ if(autocvar_g_campaign)
+ campaign_bots_may_start = true;
- Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
+ Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
- PutClientInServer(this);
+ PutClientInServer(this);
- if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
- }
- else
- stuffcmd(this, "menu_showteamselect\n");
- }
+ if(teamplay && this.team != -1)
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_PLAY_TEAM), this.netname);
else
- {
- // Player may not join because g_maxplayers is set
- Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
- }
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
+ this.team_selected = false;
}
/**
* it checks whether the number of currently playing players exceeds g_maxplayers.
* @return int number of free slots for players, 0 if none
*/
-bool nJoinAllowed(entity this, entity ignore)
+int 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 false;
+ return 0;
if(autocvar_g_forced_team_otherwise == "spectator")
- return false;
+ return 0;
}
- if(this.team_forced < 0)
- return false; // forced spectators can never join
+ if(this && this.team_forced < 0)
+ return 0; // forced spectators can never join
// TODO simplify this
int totalClients = 0;
++currentlyPlaying;
));
+ float free_slots = 0;
if (!autocvar_g_maxplayers)
- return maxclients - totalClients;
+ free_slots = maxclients - totalClients;
+ else if(currentlyPlaying < autocvar_g_maxplayers)
+ free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
- if(currentlyPlaying < autocvar_g_maxplayers)
- return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
+ static float join_prevent_msg_time = 0;
+ if(this && ignore && !free_slots && time > join_prevent_msg_time)
+ {
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
+ join_prevent_msg_time = time + 3;
+ }
- return false;
+ return free_slots;
}
/**
}
}
+bool joinAllowed(entity this)
+{
+ if (this.version_mismatch) return false;
+ if (!nJoinAllowed(this, this)) return false;
+ if (teamplay && lockteams) return false;
+ if (ShowTeamSelection(this)) return false;
+ if (MUTATOR_CALLHOOK(ForbidSpawn, this)) return false;
+ return true;
+}
+
void ObserverThink(entity this)
{
if ( this.impulse )
MinigameImpulse(this, this.impulse);
this.impulse = 0;
}
+
if (this.flags & FL_JUMPRELEASED) {
- if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
+ if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
this.flags &= ~FL_JUMPRELEASED;
this.flags |= FL_SPAWNING;
} else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
if(this.flags & FL_SPAWNING)
{
this.flags &= ~FL_SPAWNING;
- LeaveSpectatorMode(this);
+ Join(this);
return;
}
}
return;
}
}
+
if (this.flags & FL_JUMPRELEASED) {
- if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
+ if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
this.flags &= ~FL_JUMPRELEASED;
this.flags |= FL_SPAWNING;
} else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
if(this.flags & FL_SPAWNING)
{
this.flags &= ~FL_SPAWNING;
- LeaveSpectatorMode(this);
+ Join(this);
return;
}
}
if (IS_PLAYER(this)) {
CheckRules_Player(this);
- if (intermission_running) {
- IntermissionThink(this);
+ if (gameover || intermission_running) {
+ if(intermission_running)
+ IntermissionThink(this);
return;
}
{
if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
this.deadflag = DEAD_RESPAWNING;
- else if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
+ else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
this.deadflag = DEAD_DEAD;
break;
}
this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
}
- else if (gameover) {
- if (intermission_running) IntermissionThink(this);
+ else if (gameover || intermission_running) {
+ if(intermission_running)
+ IntermissionThink(this);
return;
}
else if (IS_OBSERVER(this)) {
);
}
- int oldspectatee_status = this.spectatee_status;
- if (IS_SPEC(this)) {
- this.spectatee_status = etof(this.enemy);
- } else if (IS_OBSERVER(this)) {
- this.spectatee_status = etof(this);
- } else {
- this.spectatee_status = 0;
- }
- if (this.spectatee_status != oldspectatee_status) {
- ClientData_Touch(this);
- if (g_race || g_cts) race_InitSpectator();
- }
-
if (this.teamkill_soundtime && time > this.teamkill_soundtime)
{
this.teamkill_soundtime = 0;
void Player_Physics(entity this)
{
- set_movetype(this, ((this.move_qcphysics) ? MOVETYPE_NONE : this.move_movetype));
+ set_movetype(this, this.move_movetype);
if(!this.move_qcphysics)
return;
- int mt = this.move_movetype;
-
- if(mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH || mt == MOVETYPE_PHYSICS)
- {
- this.move_qcphysics = false;
- set_movetype(this, mt);
- return;
- }
-
if(!frametime && !this.pm_frametime)
return;
CheatFrame(this);
//CheckPlayerJump();
+ if (gameover)
+ {
+ this.solid = SOLID_NOT;
+ this.takedamage = DAMAGE_NO;
+ set_movetype(this, MOVETYPE_NONE);
+ }
if (IS_PLAYER(this)) {
DrownPlayer(this);
CheckRules_Player(this);
UpdateChatBubble(this);
if (this.impulse) ImpulseCommands(this);
- if (intermission_running) return; // intermission or finale
+ if (gameover)
+ {
+ CSQCMODEL_AUTOUPDATE(this);
+ return;
+ }
GetPressedKeys(this);
}