#include <common/csqcmodel_settings.qh>
#include <common/deathtypes/all.qh>
+#include <common/debug.qh>
#include <common/effects/all.qh>
#include <common/effects/qc/globalsound.qh>
#include <common/ent_cs.qh>
#include <server/antilag.qh>
#include <server/bot/api.qh>
#include <server/bot/default/cvars.qh>
+#include <server/bot/default/waypoints.qh>
#include <server/campaign.qh>
#include <server/chat.qh>
#include <server/cheats.qh>
#include <server/clientkill.qh>
-#include <server/command/common.qh>
+#include <server/command/banning.qh>
+#include <server/command/cmd.qh>
#include <server/command/common.qh>
#include <server/command/vote.qh>
#include <server/compat/quake3.qh>
if (CS(e).race_completed) sf |= BIT(0); // forced scoreboard
if (CS(to).spectatee_status) sf |= BIT(1); // spectator ent number follows
if (CS(e).zoomstate) sf |= BIT(2); // zoomed
+ if (observe_blocked_if_eliminated && INGAME(to))
+ sf |= BIT(3); // observing blocked
if (autocvar_sv_showspectators == 1 || (autocvar_sv_showspectators && IS_SPEC(to)))
sf |= BIT(4); // show spectators
{
if (vote_called) { VoteCount(false); }
this.ready = false;
- recount_ready = true;
+ if (warmup_stage || game_starttime > time) recount_ready = true;
}
entcs_update_players(this);
}
TRANSMUTE(Observer, this);
- if(recount_ready) ReadyCount();
+ if(recount_ready) ReadyCount(); // FIXME: please add comment about why this is delayed
WaypointSprite_PlayerDead(this);
accuracy_resend(this);
setcolor(player, stof(autocvar_sv_defaultplayercolors));
}
+void GiveWarmupResources(entity this)
+{
+ SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
+ SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
+ SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
+ SetResource(this, RES_CELLS, warmup_start_ammo_cells);
+ SetResource(this, RES_PLASMA, warmup_start_ammo_plasma);
+ SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
+ SetResource(this, RES_HEALTH, warmup_start_health);
+ SetResource(this, RES_ARMOR, warmup_start_armorvalue);
+ STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
+}
+
void PutPlayerInServer(entity this)
{
if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
this.takedamage = DAMAGE_AIM;
this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
- if (warmup_stage) {
- SetResource(this, RES_SHELLS, warmup_start_ammo_shells);
- SetResource(this, RES_BULLETS, warmup_start_ammo_nails);
- SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets);
- SetResource(this, RES_CELLS, warmup_start_ammo_cells);
- SetResource(this, RES_PLASMA, warmup_start_ammo_plasma);
- SetResource(this, RES_FUEL, warmup_start_ammo_fuel);
- SetResource(this, RES_HEALTH, warmup_start_health);
- SetResource(this, RES_ARMOR, warmup_start_armorvalue);
- STAT(WEAPONS, this) = WARMUP_START_WEAPONS;
- } else {
+ if (warmup_stage)
+ GiveWarmupResources(this);
+ else
+ {
SetResource(this, RES_SHELLS, start_ammo_shells);
SetResource(this, RES_BULLETS, start_ammo_nails);
SetResource(this, RES_ROCKETS, start_ammo_rockets);
this.respawn_flags = 0;
this.respawn_time = 0;
STAT(RESPAWN_TIME, this) = 0;
- this.scale = ((q3compat && autocvar_sv_q3compat_changehitbox) ? 0.9 : autocvar_sv_player_scale);
+ // DP model scaling uses 1/16 accuracy and 13/16 is closest to 56/69
+ this.scale = ((q3compat && autocvar_sv_q3compat_changehitbox) ? 0.8125 : autocvar_sv_player_scale);
this.fade_time = 0;
this.pain_finished = 0;
this.pushltime = 0;
this.alivetime = time;
antilag_clear(this, CS(this));
+
+ if (warmup_stage < 0 || warmup_stage > 1)
+ ReadyCount();
}
/** Called when a client spawns in the server */
return false; // empty list or search, just return
// this function allows abbreviated strings!
- FOREACH_WORD(list, it == substring(tofind, 0, strlen(it)),
+ FOREACH_WORD(list, it != "" && it == substring(tofind, 0, strlen(it)),
{
return true;
});
bool PlayerInList(entity player, string list)
{
+ if (list == "")
+ return false;
return boolean(PlayerInIDList(player, list) || PlayerInIPList(player, list));
}
WriteByte(msg_type, boolean(autocvar_g_campaign));
if (boolean(autocvar_g_campaign))
{
- WriteString(msg_type, Campaign_GetTitle());
WriteByte(msg_type, Campaign_GetLevelNum());
- WriteString(msg_type, Campaign_GetMessage());
return;
}
WriteString(msg_type, autocvar_hostname);
WriteString(msg_type, autocvar_g_xonoticversion);
WriteByte(msg_type, CS(this).version_mismatch);
WriteByte(msg_type, (CS(this).version < autocvar_gameversion));
+ WriteByte(msg_type, autocvar_g_warmup > 1 ? autocvar_g_warmup : map_minplayers);
+ WriteByte(msg_type, GetPlayerLimit());
MUTATOR_CALLHOOK(BuildMutatorsPrettyString, "");
string modifications = M_ARGV(0, string);
{
if (g_weaponarena_weapons == WEPSET(TUBA))
stuffcmd(this, "cl_cmd settemp chase_active 1\n");
+ // quickmenu file must be put in a subfolder with an unique name
+ // to reduce chances of overriding custom client quickmenus
+ if (waypointeditor_enabled)
+ stuffcmd(this, sprintf("cl_cmd settemp _hud_panel_quickmenu_file_from_server %s\n", "wpeditor.txt"));
+ else if (autocvar_sv_quickmenu_file != "" && strstrofs(autocvar_sv_quickmenu_file, "/", 0) && fexists(autocvar_sv_quickmenu_file))
+ stuffcmd(this, sprintf("cl_cmd settemp _hud_panel_quickmenu_file_from_server %s\n", autocvar_sv_quickmenu_file));
}
if (!autocvar_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 && AvailableTeams() == 2))
+ if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AVAILABLE_TEAMS == 2))
if(!MUTATOR_CALLHOOK(HideTeamNagger, this))
send_CSQC_teamnagger();
if (IS_REAL_CLIENT(this))
sv_notice_join(this);
- this.move_qcphysics = autocvar_sv_qcphysics;
+ this.move_qcphysics = true;
// update physics stats (players can spawn before physics runs)
Physics_UpdateStats(this);
Handicap_Initialize(this);
+ // playban
+ if (PlayerInList(this, autocvar_g_playban_list))
+ TRANSMUTE(Observer, this);
+
+ if (PlayerInList(this, autocvar_g_chatban_list)) // chatban
+ CS(this).muted = true;
+
MUTATOR_CALLHOOK(ClientConnect, this);
if (player_count == 1)
+ {
+ if (autocvar_sv_autopause && server_is_dedicated)
+ setpause(0);
localcmd("\nsv_hook_firstjoin\n");
+ }
}
/*
=============
{
assert(IS_CLIENT(this), return);
+ /* from "ignore" command */
+ strfree(this.ignore_list);
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) && it.ignore_list,
+ {
+ if(it.crypto_idfp && it.crypto_idfp != "")
+ continue;
+ string mylist = ignore_removefromlist(it, this);
+ if(it.ignore_list)
+ strunzone(it.ignore_list);
+
+ it.ignore_list = strzone(mylist);
+ });
+ /* from "ignore" command */
+
PlayerStats_GameReport_FinalizePlayer(this);
if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
if (CS(this).active_minigame) part_minigame(this);
if (this.personal) delete(this.personal);
this.playerid = 0;
- ReadyCount();
+ if (warmup_stage || game_starttime > time) ReadyCount();
if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
player_powerups_remove_all(this); // stop powerup sound
if (!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (CS(this).wasplayer && autocvar_g_changeteam_banned) || Player_HasRealForcedTeam(this))
return false;
if (frametime) // once per frame is more than enough
- stuffcmd(this, "scoreboard_team_selection\n");
+ stuffcmd(this, "_scoreboard_team_selection 1\n");
return true;
}
void Join(entity this)
{
if(g_duel)
return 2; // TODO: this workaround is needed since the mutator hook from duel can't be activated before the gametype is loaded (e.g. switching modes via gametype vote screen)
- int player_limit = autocvar_g_maxplayers;
+ // don't return map_maxplayers during intermission as it would interfere with MapHasRightSize()
+ int player_limit = (autocvar_g_maxplayers >= 0 || intermission_running) ? autocvar_g_maxplayers : map_maxplayers;
MUTATOR_CALLHOOK(GetPlayerLimit, player_limit);
player_limit = M_ARGV(0, int);
- return player_limit;
+ return player_limit < maxclients ? player_limit : 0;
}
/**
if(this && (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR))
return 0; // forced spectators can never join
+ static float msg_time = 0;
+ if(this && !INGAME(this) && ignore && PlayerInList(this, autocvar_g_playban_list))
+ {
+ if(time > msg_time)
+ {
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PLAYBAN);
+ msg_time = time + 0.5;
+ }
+ return 0;
+ }
+
// TODO simplify this
int totalClients = 0;
int currentlyPlaying = 0;
else if(player_limit > 0 && currentlyPlaying < player_limit)
free_slots = min(maxclients - totalClients, player_limit - currentlyPlaying);
- static float msg_time = 0;
if(this && !INGAME(this) && ignore && !free_slots && time > msg_time)
{
- Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
+ Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT, player_limit);
msg_time = time + 0.5;
}
return true;
}
+void show_entnum(entity this)
+{
+ // waypoint editor implements a similar feature for waypoints
+ if (waypointeditor_enabled)
+ return;
+
+ if (wasfreed(this.wp_aimed))
+ this.wp_aimed = NULL;
+
+ WarpZone_crosshair_trace_plusvisibletriggers(this);
+ entity ent = NULL;
+ if (trace_ent)
+ {
+ ent = trace_ent;
+ if (ent != this.wp_aimed)
+ {
+ string str = sprintf(
+ "^7ent #%d\n^8 netname: ^3%s\n^8 classname: ^5%s\n^8 origin: ^2'%s'",
+ etof(ent), ent.netname, ent.classname, vtos(ent.origin));
+ debug_text_3d((ent.absmin + ent.absmax) * 0.5, str, 0, 7, '0 0 0');
+ }
+ }
+ if (this.wp_aimed != ent)
+ this.wp_aimed = ent;
+}
+
.string shootfromfixedorigin;
.bool dualwielding_prev;
bool PlayerThink(entity this)
if (frametime) player_powerups(this);
+ if (frametime && autocvar_sv_show_entnum) show_entnum(this);
+
if (IS_DEAD(this)) {
if (this.personal && g_race_qualifying) {
if (time > this.respawn_time) {
}
}
+ if (frametime && autocvar_sv_show_entnum) show_entnum(this);
+
if (IS_BOT_CLIENT(this) && !CS(this).autojoin_checked)
{
CS(this).autojoin_checked = true;
TRANSMUTE(Player, this);
PutClientInServer(this);
+
+ .entity weaponentity = weaponentities[0];
+ if(this.(weaponentity).m_weapon == WEP_Null)
+ W_NextWeapon(this, 0, weaponentity);
+
return;
}
}
CS(this).impulse = 0;
} else if(PHYS_INPUT_BUTTON_ATCK2(this)) {
- this.would_spectate = false;
- this.flags &= ~FL_JUMPRELEASED;
- TRANSMUTE(Observer, this);
- PutClientInServer(this);
+ if(!observe_blocked_if_eliminated || !INGAME(this)) {
+ this.would_spectate = false;
+ this.flags &= ~FL_JUMPRELEASED;
+ TRANSMUTE(Observer, this);
+ PutClientInServer(this);
+ }
} else if(!SpectateUpdate(this) && !SpectateNext(this)) {
PutObserverInServer(this, false, true);
this.would_spectate = true;
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;
+ // primary attack pressed
if(this.flags & FL_SPAWNING)
{
this.flags &= ~FL_SPAWNING;