- wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache
- wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired
- make
- - EXPECT=04d63df6a5d73bd335612543efd944d6
+ - EXPECT=b58f9c7587f1a14e5c52176d4e62a9fb
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
set bot_ai_navigation_jetpack_mindistance 3500 "Bots will try fly to objects located farther than this distance"
// Better don't touch these, there are hard to tweak!
set bot_ai_aimskill_order_mix_1st 0.01 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 1st filter output to apply to the aiming angle"
-set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 1st filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 2nd filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 3th filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 4th filter output to apply to the aiming angle"
+set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 5th filter output to apply to the aiming angle"
set bot_ai_aimskill_order_filter_1st 0.4 "Position filter"
set bot_ai_aimskill_order_filter_2nd 0.4 "Movement filter"
set bot_ai_aimskill_order_filter_3th 0.2 "Acceleration filter"
seta notification_INFO_WEAPON_VAPORIZER_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_WEAPON_VORTEX_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
-// MSG_CENTER notifications (count = 230):
+// MSG_CENTER notifications (count = 231):
seta notification_CENTER_ALONE "1" "0 = off, 1 = centerprint"
seta notification_CENTER_ASSAULT_ATTACKING "1" "0 = off, 1 = centerprint"
seta notification_CENTER_ASSAULT_DEFENDING "1" "0 = off, 1 = centerprint"
+seta notification_CENTER_ASSAULT_OBJ_DESTROYED "1" "0 = off, 1 = centerprint"
seta notification_CENTER_CAMPCHECK "1" "0 = off, 1 = centerprint"
seta notification_CENTER_COINTOSS "1" "0 = off, 1 = centerprint"
seta notification_CENTER_COUNTDOWN_BEGIN "1" "0 = off, 1 = centerprint"
seta notification_show_sprees_info_newline "1" "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
seta notification_show_sprees_info_specialonly "1" "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
-// Notification counts (total = 820): MSG_ANNCE = 89, MSG_INFO = 320, MSG_CENTER = 230, MSG_MULTI = 153, MSG_CHOICE = 28
+// Notification counts (total = 821): MSG_ANNCE = 89, MSG_INFO = 320, MSG_CENTER = 231, MSG_MULTI = 153, MSG_CHOICE = 28
float uid2name_dialog;
-float gameover_time;
+float intermission_time;
.bool csqcmodel_isdead; // used by shownames and miscfunctions (entcs_IsDead) to know when a player is dead
}
vector timer_color;
- if(gameover_time || minutesLeft >= 5 || warmup_stage || timelimit == 0)
+ if(intermission_time || minutesLeft >= 5 || warmup_stage || timelimit == 0)
timer_color = '1 1 1'; //white
else if(minutesLeft >= 1)
timer_color = '1 1 0'; //yellow
else
timer_color = '1 0 0'; //red
- if (gameover_time) {
- timer = seconds_tostring(max(0, floor(gameover_time - STAT(GAMESTARTTIME))));
+ if (intermission_time) {
+ timer = seconds_tostring(max(0, floor(intermission_time - STAT(GAMESTARTTIME))));
} else if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) {
if (time < STAT(GAMESTARTTIME))
timer = seconds_tostring(0); //while restart is still active, show 00:00
hit = !(trace_fraction < 1 && (trace_networkentity != this.sv_entnum && trace_ent.entnum != this.sv_entnum));
}
// handle tag fading
- bool overlap = false;
+ int overlap = -1;
vector o = project_3d_to_2d(this.origin + eZ * autocvar_hud_shownames_offset);
+ if (autocvar_hud_shownames_crosshairdistance)
+ {
+ float d = autocvar_hud_shownames_crosshairdistance;
+ float w = o.x - vid_conwidth / 2;
+ float h = o.y - vid_conheight / 2;
+ if (d * d > w * w + h * h) this.pointtime = time;
+ if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time)
+ overlap = 1;
+ else if(!autocvar_hud_shownames_crosshairdistance_antioverlap)
+ overlap = 0;
+ }
+
float dist = vlen(this.origin - view_origin);
- if (autocvar_hud_shownames_antioverlap)
+ if (overlap == -1 && autocvar_hud_shownames_antioverlap)
{
// fade tag out if another tag that is closer to you overlaps
- LL_EACH(shownames_ent, it != this && entcs_receiver(i), {
+ entity entcs = NULL;
+ LL_EACH(shownames_ent, it != this, {
+ entcs = entcs_receiver(i);
+ if (!(entcs && entcs.has_sv_origin))
+ continue;
vector eo = project_3d_to_2d(it.origin);
if (eo.z < 0 || eo.x < 0 || eo.y < 0 || eo.x > vid_conwidth || eo.y > vid_conheight) continue;
eo.z = 0;
if (vdist(((eX * o.x + eY * o.y) - eo), <, autocvar_hud_shownames_antioverlap_distance)
&& vdist((it.origin - view_origin), <, dist))
{
- overlap = true;
+ overlap = 1;
break;
}
});
}
bool onscreen = (o.z >= 0 && o.x >= 0 && o.y >= 0 && o.x <= vid_conwidth && o.y <= vid_conheight);
- if (autocvar_hud_shownames_crosshairdistance)
- {
- float d = autocvar_hud_shownames_crosshairdistance;
- float w = o.x - vid_conwidth / 2;
- float h = o.y - vid_conheight / 2;
- if (d * d > w * w + h * h) this.pointtime = time;
- if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) overlap = true;
- else overlap = (autocvar_hud_shownames_crosshairdistance_antioverlap ? overlap : false); // override what antioverlap says unless allowed by cvar.
- }
if (!this.fadedelay) this.fadedelay = time + SHOWNAMES_FADEDELAY;
if (this.csqcmodel_isdead) // dead player, fade out slowly
{
this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime);
this.fadedelay = 0; // reset fade in delay, enemy has left the view
}
- else if (overlap) // tag overlap detected, fade out
+ else if (overlap > 0) // tag overlap detected, fade out
{
this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime);
}
it.sameteam = false;
}
bool dead = entcs_IsDead(i) || entcs_IsSpectating(i);
- if(gametype == MAPINFO_TYPE_CA)
- dead = (dead || entcs_IsEliminated(i));
if (!it.csqcmodel_isdead) setorigin(it, entcs.origin);
it.csqcmodel_isdead = dead;
Draw_ShowNames(it);
{
if(autocvar_cl_orthoview)
return false;
- if(intermission)
+ if(STAT(GAMEOVER) || intermission)
return true;
if(this.viewloc)
return true;
{
float f, i, j;
vector v;
- if(!scoreboard_active && !camera_active && intermission != 2 &&
+ if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAMEOVER) &&
spectatee_status != -1 && !csqcplayer.viewloc && !MUTATOR_CALLHOOK(DrawCrosshair) &&
!HUD_MinigameMenu_IsOpened() )
{
if(!postinit)
PostInit();
- if(intermission && !gameover_time)
- gameover_time = time;
+ if(intermission && !intermission_time)
+ intermission_time = time;
if(intermission && !isdemo() && !(calledhooks & HOOK_END))
{
entity player = this.owner;
sf |= BIT(0); // assume private
do {
+ if (!(IS_PLAYER(player)))
+ {
+ sf &= ENTCS_PUBLICMASK; // no private updates
+ break;
+ }
if (radar_showennemies) break;
if (SAME_TEAM(to, player)) break;
if (!(IS_PLAYER(to) || to.caplayer) && time > game_starttime) break;
/**
* @param i zero indexed player
- */
- .int frags;
- bool entcs_IsEliminated(int i)
- {
- bool unconnected = !playerslots[i].gotscores;
- entity e = entcs_receiver(i);
- return unconnected || ((e) ? e.frags : stof(getplayerkeyvalue(i, "frags"))) == FRAGS_LMS_LOSER;
- }
-
- /**
- * @param i zero indexed player
*/
int entcs_GetClientColors(int i)
{
this.m_waypoint = _("Mega armor");
this.m_waypointblink = 2;
#ifdef SVQC
+ this.m_maxs = '16 16 70';
this.m_botvalue = BOT_PICKUP_RATING_HIGH;
this.m_itemid = IT_ARMOR;
this.m_respawntime = GET(g_pickup_respawntime_long);
this.m_waypoint = _("Mega health");
this.m_waypointblink = 2;
#ifdef SVQC
+ this.m_maxs = '16 16 70';
this.m_botvalue = BOT_PICKUP_RATING_HIGH;
this.m_itemid = IT_HEALTH;
this.m_respawntime = GET(g_pickup_respawntime_long);
CLASS(Powerup, Pickup)
#ifdef SVQC
ATTRIB(Powerup, m_mins, vector, '-16 -16 0');
- ATTRIB(Powerup, m_maxs, vector, '16 16 48');
+ ATTRIB(Powerup, m_maxs, vector, '16 16 80');
ATTRIB(Powerup, m_botvalue, int, 100000);
ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
this.skin = buff.m_skin;
setmodel(this, MDL_BUFF);
+ setsize(this, BUFF_MIN, BUFF_MAX);
if(this.buff_waypoint)
{
.int oldbuffs; // for updating effects
.entity buff_model; // controls effects (TODO: make csqc)
-const vector BUFF_MIN = ('-16 -16 -20');
-const vector BUFF_MAX = ('16 16 20');
+const vector BUFF_MIN = ('-16 -16 0');
+const vector BUFF_MAX = ('16 16 60');
// client side options
.float cvar_cl_buffs_autoreplace;
{
entity player = M_ARGV(0, entity);
- if(!intermission_running)
+ if(!gameover)
if(!IS_DEAD(player))
if(IS_PLAYER(player))
NIX_GiveCurrentWeapon(player);
MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
{
- if(intermission_running || gameover)
+ if(gameover)
return;
entity player = M_ARGV(0, entity);
MSG_CENTER_NOTIF(ASSAULT_ATTACKING, 1, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are attacking!"), "")
MSG_CENTER_NOTIF(ASSAULT_DEFENDING, 1, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are defending!"), "")
+ MSG_CENTER_NOTIF(ASSAULT_OBJ_DESTROYED, 1, 0, 1, "f1time", CPID_ASSAULT_ROLE, "0 0", _("^BGObjective destroyed in ^F4%s^BG!"), "")
MSG_CENTER_NOTIF(COUNTDOWN_BEGIN, 1, 0, 0, "", CPID_ROUND, "2 0", _("^F4Begin!"), "")
MSG_CENTER_NOTIF(COUNTDOWN_GAMESTART, 1, 0, 1, "", CPID_ROUND, "1 f1", _("^F4Game starts in ^COUNT"), "")
#ifdef SVQC
SPECTATE_COPYFIELD(_STAT(WEAPON_NEXTTHINK))
float W_WeaponRateFactor(entity this);
+float gameover;
#endif
REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
-
+REGISTER_STAT(GAMEOVER, int, gameover)
REGISTER_STAT(GAMESTARTTIME, float)
REGISTER_STAT(STRENGTH_FINISHED, float)
REGISTER_STAT(INVINCIBLE_FINISHED, float)
entity vehic = this.vehicle;
return = true;
- if(intermission_running)
+ if(gameover)
{
vehic.solid = SOLID_NOT;
vehic.takedamage = DAMAGE_NO;
entity vehic = this.vehicle;
return = true;
- if(intermission_running)
+ if(gameover)
{
vehic.solid = SOLID_NOT;
vehic.takedamage = DAMAGE_NO;
entity vehic = this.vehicle;
return = true;
- if(intermission_running)
+ if(gameover)
{
vehic.solid = SOLID_NOT;
vehic.takedamage = DAMAGE_NO;
entity vehic = this.vehicle;
return = true;
- if(intermission_running)
+ if(gameover)
{
vehic.solid = SOLID_NOT;
vehic.takedamage = DAMAGE_NO;
}
void XonoticCreditsList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
{
- // layout: Ping, Credits name, Map name, NP, TP, MP
string s;
float theAlpha;
vector theColor;
t.configureXonoticTextSliderValues(t);
}
+void GameType_ConfigureSliders_for_CurrentGametype(entity me)
+{
+ switch(MapInfo_CurrentGametype())
+ {
+ case MAPINFO_TYPE_CA: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_ca_teams_override", _("The amount of frags needed before the match will end")); break;
+ case MAPINFO_TYPE_FREEZETAG: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_freezetag_teams_override", _("The amount of frags needed before the match will end")); break;
+ case MAPINFO_TYPE_CTF: GameType_ConfigureSliders(me, _("Capture limit:"), 1, 20, 1, "capturelimit_override", string_null, _("The amount of captures needed before the match will end")); break;
+ case MAPINFO_TYPE_DOMINATION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, "g_domination_point_limit", "g_domination_teams_override", _("The amount of points needed before the match will end")); break;
+ case MAPINFO_TYPE_KEYHUNT: GameType_ConfigureSliders(me, _("Point limit:"), 200, 1500, 50, "g_keyhunt_point_limit", "g_keyhunt_teams_override", _("The amount of points needed before the match will end")); break;
+ case MAPINFO_TYPE_LMS: GameType_ConfigureSliders(me, _("Lives:"), 3, 50, 1, "g_lms_lives_override", string_null, string_null); break;
+ case MAPINFO_TYPE_RACE: GameType_ConfigureSliders(me, _("Laps:"), 1, 25, 1, "g_race_laps_limit", string_null, string_null); break;
+ case MAPINFO_TYPE_NEXBALL: GameType_ConfigureSliders(me, _("Goals:"), 1, 50, 1, "g_nexball_goallimit", string_null, _("The amount of goals needed before the match will end")); break;
+ case MAPINFO_TYPE_ASSAULT: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
+ case MAPINFO_TYPE_ONSLAUGHT: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
+ case MAPINFO_TYPE_CTS: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
+ case MAPINFO_TYPE_INVASION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
+ case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"), 5, 100, 5, "g_tdm_point_limit", "g_tdm_teams_override", _("The amount of points needed before the match will end")); break;
+ default: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", string_null, _("The amount of frags needed before the match will end")); break;
+ }
+}
+
entity makeXonoticServerCreateTab()
{
entity me;
e.onClickEntity = me.mapListBox;
me.mapListBox.startButton = e;
- me.gameTypeChangeNotify(me);
+ GameType_ConfigureSliders_for_CurrentGametype(me);
}
void XonoticServerCreateTab_gameTypeChangeNotify(entity me)
{
- switch(MapInfo_CurrentGametype())
- {
- case MAPINFO_TYPE_CA: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_ca_teams_override", _("The amount of frags needed before the match will end")); break;
- case MAPINFO_TYPE_FREEZETAG: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_freezetag_teams_override", _("The amount of frags needed before the match will end")); break;
- case MAPINFO_TYPE_CTF: GameType_ConfigureSliders(me, _("Capture limit:"), 1, 20, 1, "capturelimit_override", string_null, _("The amount of captures needed before the match will end")); break;
- case MAPINFO_TYPE_DOMINATION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, "g_domination_point_limit", "g_domination_teams_override", _("The amount of points needed before the match will end")); break;
- case MAPINFO_TYPE_KEYHUNT: GameType_ConfigureSliders(me, _("Point limit:"), 200, 1500, 50, "g_keyhunt_point_limit", "g_keyhunt_teams_override", _("The amount of points needed before the match will end")); break;
- case MAPINFO_TYPE_LMS: GameType_ConfigureSliders(me, _("Lives:"), 3, 50, 1, "g_lms_lives_override", string_null, string_null); break;
- case MAPINFO_TYPE_RACE: GameType_ConfigureSliders(me, _("Laps:"), 1, 25, 1, "g_race_laps_limit", string_null, string_null); break;
- case MAPINFO_TYPE_NEXBALL: GameType_ConfigureSliders(me, _("Goals:"), 1, 50, 1, "g_nexball_goallimit", string_null, _("The amount of goals needed before the match will end")); break;
- case MAPINFO_TYPE_ASSAULT: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
- case MAPINFO_TYPE_ONSLAUGHT: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
- case MAPINFO_TYPE_CTS: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
- case MAPINFO_TYPE_INVASION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
- case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"), 5, 100, 5, "g_tdm_point_limit", "g_tdm_teams_override", _("The amount of points needed before the match will end")); break;
- default: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", string_null, _("The amount of frags needed before the match will end")); break;
- }
+ GameType_ConfigureSliders_for_CurrentGametype(me);
me.mapListBox.refilter(me.mapListBox);
}
if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
{
+ this.movement = '0 0 0';
this.bot_nextthink = time + 0.5;
return;
}
// if dead, just wait until we can respawn
if (IS_DEAD(this))
{
+ this.movement = '0 0 0';
if (this.deadflag == DEAD_DEAD)
{
PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
void bot_serverframe()
{
- if (intermission_running)
+ if (gameover)
return;
if (time < 2)
// TODO: tracewalk() should take care of this job (better path finding under water)
// if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
- if(IS_DEAD(this))
+ if(!(IS_DEAD(this)))
if(!this.goalcurrent)
if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
{
if (!bot_waypoint_queue_owner)
{
- LOG_DEBUG(this.netname, " sutck, taking over the waypoints queue");
+ LOG_DEBUG(this.netname, " stuck, taking over the waypoints queue");
bot_waypoint_queue_owner = this;
bot_waypoint_queue_bestgoal = NULL;
bot_waypoint_queue_bestgoalrating = 0;
if (bot_waypoint_queue_goal)
{
// evaluate the next goal on the queue
- float d = vlen(this.origin - bot_waypoint_queue_goal.origin);
+ float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d));
if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
{
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) {
if (IS_PLAYER(this)) {
CheckRules_Player(this);
- if (intermission_running) {
- IntermissionThink(this);
+ if (gameover || intermission_running) {
+ if(intermission_running)
+ IntermissionThink(this);
return;
}
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)) {
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);
}
void ReadyRestart()
{
// no assault support yet...
- if (g_assault | gameover | intermission_running | race_completing) localcmd("restart\n");
+ if (g_assault || gameover || race_completing) localcmd("restart\n");
else localcmd("\nsv_hook_gamerestart\n");
// Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
void weapon_defaultspawnfunc(entity this, Weapon e);
-float gameover;
float intermission_running;
float intermission_exittime;
float alreadychangedlevel;
error("Owner lost the hook!\n");
return;
}
- if(LostMovetypeFollow(this) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
+ if(LostMovetypeFollow(this) || gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade"))
{
RemoveGrapplingHook(this.realowner);
return;
void IntermissionThink(entity this)
{
FixIntermissionClient(this);
- CSQCMODEL_AUTOUPDATE(this); // PlayerPostThink returns before calling this during intermission, so run it here
float server_screenshot = (autocvar_sv_autoscreenshot && this.cvar_cl_autoscreenshot);
float client_screenshot = (this.cvar_cl_autoscreenshot == 2);
e.autoscreenshot = time + 0.8; // used for autoscreenshot
e.health = -2342;
// first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not)
- e.solid = SOLID_NOT;
- set_movetype(e, MOVETYPE_NONE);
- e.takedamage = DAMAGE_NO;
for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
.entity weaponentity = weaponentities[slot];
intermission_running = 1;
-// enforce a wait time before allowing changelevel
+ // enforce a wait time before allowing changelevel
if(player_count > 0)
intermission_exittime = time + autocvar_sv_mapchange_delay;
else
SetDefaultAlpha();
- if (gameover) // someone else quit the game already
+ if (intermission_running) // someone else quit the game already
{
if(player_count == 0) // Nobody there? Then let's go to the next map
MapVote_Start();
string playername(entity p)
{
string t;
- if (teamplay && !intermission_running && IS_PLAYER(p))
+ if (teamplay && !gameover && IS_PLAYER(p))
{
t = Team_ColorCode(p.team);
return strcat(t, strdecolorize(p.netname));
/** keepvelocity? */ i(bool, MUTATOR_ARGV_2_bool) \
/**/
MUTATOR_HOOKABLE(CopyBody, EV_CopyBody);
+
+/** called when sending a chat message, ret argument can be changed to prevent the message */
+#define EV_ChatMessage(i, o) \
+ /** sender */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** ret */ i(int, MUTATOR_ARGV_1_int) \
+ /**/ o(int, MUTATOR_ARGV_1_int) \
+ /**/
+MUTATOR_HOOKABLE(ChatMessage, EV_ChatMessage);
+
+/** return true to prevent sending a chat (private, team or regular) message from reaching a certain player */
+#define EV_ChatMessageTo(i, o) \
+ /** destination player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** sender */ i(entity, MUTATOR_ARGV_1_entity) \
+ /**/
+MUTATOR_HOOKABLE(ChatMessageTo, EV_ChatMessageTo);
#include "gamemode_assault.qh"
.entity sprite;
+#define AS_ROUND_DELAY 5
// random functions
void assault_objective_use(entity this, entity actor, entity trigger)
});
// reset the level with a countdown
- cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
+ cvar_set("timelimit", ftos(ceil(time - AS_ROUND_DELAY - game_starttime) / 60));
ReadyRestart_force(); // sets game_starttime
}
+entity as_round;
+.entity ent_winning;
+void as_round_think()
+{
+ gameover = false;
+ assault_new_round(as_round.ent_winning);
+ delete(as_round);
+ as_round = NULL;
+}
+
// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
// they win. Otherwise the defending team wins once the timelimit passes.
int WinningCondition_Assault()
{
+ if(as_round)
+ return WINNING_NO;
+
WinningConditionHelper(NULL); // set worldstatus
int status = WINNING_NO;
{
if(ent.winning) // round end has been triggered by attacking team
{
- bprint("ASSAULT: round completed...\n");
+ bprint("Assault: round completed.\n");
SetWinners(team, assault_attacker_team);
TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
}
else
{
- assault_new_round(ent);
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ASSAULT_OBJ_DESTROYED, ceil(time - game_starttime));
+ as_round = new(as_round);
+ as_round.think = as_round_think;
+ as_round.ent_winning = ent;
+ as_round.nextthink = time + AS_ROUND_DELAY;
+ gameover = true;
+
+ // make sure timelimit isn't hit while the game is blocked
+ if(autocvar_timelimit > 0)
+ if(time + AS_ROUND_DELAY >= game_starttime + autocvar_timelimit * 60)
+ cvar_set("timelimit", ftos(autocvar_timelimit + AS_ROUND_DELAY / 60));
}
}
}
// bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self
// bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self
// bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self
-.float kh_state = _STAT(KH_KEYS);
+.int kh_state = _STAT(KH_KEYS);
.float siren_time; // time delay the siren
//.float stuff_time; // time delay to stuffcmd a cvar
-float kh_keystatus[17];
+int kh_keystatus[17];
//kh_keystatus[0] = status of dropped keys, kh_keystatus[1 - 16] = player #
//replace 17 with cvar("maxplayers") or similar !!!!!!!!!
//for(i = 0; i < maxplayers; ++i)
// kh_keystatus[i] = "0";
-float kh_Team_ByID(float t)
+int kh_Team_ByID(int t)
{
if(t == 0) return NUM_TEAM_1;
if(t == 1) return NUM_TEAM_2;
//entity kh_worldkeylist;
.entity kh_worldkeynext;
entity kh_controller;
-//float kh_tracking_enabled;
-float kh_teams;
-float kh_interferemsg_time, kh_interferemsg_team;
+//bool kh_tracking_enabled;
+int kh_teams;
+int kh_interferemsg_team;
+float kh_interferemsg_time;
.entity kh_next, kh_prev; // linked list
.float kh_droptime;
-.float kh_dropperteam;
+.int kh_dropperteam;
.entity kh_previous_owner;
-.float kh_previous_owner_playerid;
+.int kh_previous_owner_playerid;
-float kh_key_dropped, kh_key_carried;
+int kh_key_dropped, kh_key_carried;
int kh_Key_AllOwnedByWhichTeam();
void kh_update_state()
{
entity key;
- float s;
- float f;
-
- s = 0;
+ int f;
+ int s = 0;
FOR_EACH_KH_KEY(key)
{
if(key.owner)
void kh_WaitForPlayers();
void kh_Controller_Think(entity this) // called a lot
{
- if(intermission_running)
+ if(gameover)
return;
if(this.cnt > 0)
- { if(getthink(this) != kh_WaitForPlayers) { this.cnt -= 1; } }
+ {
+ if(getthink(this) != kh_WaitForPlayers)
+ this.cnt -= 1;
+ }
else if(this.cnt == 0)
{
this.cnt -= 1;
void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner) // update the score when a key is captured
{
string s;
- if(intermission_running)
+ if(gameover)
return;
if(frags_player)
void kh_Key_Attach(entity key) // runs when a player picks up a key and several times when a key is assigned to a player at the start of a round
{
#ifdef KH_PLAYER_USE_ATTACHMENT
- entity first;
- first = key.owner.kh_next;
+ entity first = key.owner.kh_next;
if(key == first)
{
setattachment(key, key.owner, KH_PLAYER_ATTACHMENT_BONE);
void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs several times times when all the keys are captured
{
#ifdef KH_PLAYER_USE_ATTACHMENT
- entity first;
- first = key.owner.kh_next;
+ entity first = key.owner.kh_next;
if(key == first)
{
if(key.kh_next)
void kh_Key_AssignTo(entity key, entity player) // runs every time a key is picked up or assigned. Runs prior to kh_key_attach
{
- entity k;
- float ownerteam0, ownerteam;
if(key.owner == player)
return;
- ownerteam0 = kh_Key_AllOwnedByWhichTeam();
+ int ownerteam0 = kh_Key_AllOwnedByWhichTeam();
if(key.owner)
{
key.pusher = NULL;
- ownerteam = kh_Key_AllOwnedByWhichTeam();
+ int ownerteam = kh_Key_AllOwnedByWhichTeam();
if(ownerteam != ownerteam0)
{
+ entity k;
if(ownerteam != -1)
{
kh_interferemsg_time = time + 0.2;
kh_interferemsg_team = player.team;
- // audit all key carrier sprites, update them to RUN HERE
+ // audit all key carrier sprites, update them to "Run here"
FOR_EACH_KH_KEY(k)
{
if (!k.owner) continue;
{
kh_interferemsg_time = 0;
- // audit all key carrier sprites, update them to RUN HERE
+ // audit all key carrier sprites, update them to "Key Carrier"
FOR_EACH_KH_KEY(k)
{
if (!k.owner) continue;
void kh_Key_Touch(entity this, entity toucher) // runs many, many times when a key has been dropped and can be picked up
{
- if(intermission_running)
+ if(gameover)
return;
if(this.owner) // already carried
void kh_Key_Remove(entity key) // runs after when all the keys have been collected or when a key has been dropped for more than X seconds
{
- entity o;
- o = key.owner;
+ entity o = key.owner;
kh_Key_AssignTo(key, NULL);
if(o) // it was attached
WaypointSprite_Kill(key.waypointsprite_attachedforcarrier);
void nades_GiveBonus(entity player, float score);
-void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TEEM?!?! what the fuck is wrong with you people
+void kh_WinnerTeam(int winner_team) // runs when a team wins
{
// all key carriers get some points
- vector firstorigin, lastorigin, midpoint;
- float first;
entity key;
- float score;
- score = (NumTeams(kh_teams) - 1) * autocvar_g_balance_keyhunt_score_capture;
+ float score = (NumTeams(kh_teams) - 1) * autocvar_g_balance_keyhunt_score_capture;
DistributeEvenly_Init(score, NumTeams(kh_teams));
// twice the score for 3 team games, three times the score for 4 team games!
// note: for a win by destroying the key, this should NOT be applied
FOR_EACH_KH_KEY(key)
{
- float f;
- f = DistributeEvenly_Get(1);
+ float f = DistributeEvenly_Get(1);
kh_Scores_Event(key.owner, key, "capture", f, 0);
PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1);
nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high);
}
- first = true;
+ bool first = true;
string keyowner = "";
FOR_EACH_KH_KEY(key)
if(key.owner.kh_next == key)
first = false;
}
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(teem, INFO_KEYHUNT_CAPTURE), keyowner);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_KEYHUNT_CAPTURE), keyowner);
first = true;
- midpoint = '0 0 0';
- firstorigin = '0 0 0';
- lastorigin = '0 0 0';
+ vector firstorigin = '0 0 0', lastorigin = '0 0 0', midpoint = '0 0 0';
FOR_EACH_KH_KEY(key)
{
- vector thisorigin;
-
- thisorigin = kh_AttachedOrigin(key);
+ vector thisorigin = kh_AttachedOrigin(key);
//dprint("Key origin: ", vtos(thisorigin), "\n");
midpoint += thisorigin;
te_lightning2(NULL, lastorigin, firstorigin);
}
midpoint = midpoint * (1 / NumTeams(kh_teams));
- te_customflash(midpoint, 1000, 1, Team_ColorRGB(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component
+ te_customflash(midpoint, 1000, 1, Team_ColorRGB(winner_team) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component
play2all(SND(KH_CAPTURE));
kh_FinishRound();
}
-void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a flag carrier off the map
+void kh_LoserTeam(int loser_team, entity lostkey) // runs when a player pushes a flag carrier off the map
{
- entity key, attacker;
- float players;
- float keys;
float f;
-
- attacker = NULL;
+ entity attacker = NULL;
if(lostkey.pusher)
- if(lostkey.pusher.team != teem)
+ if(lostkey.pusher.team != loser_team)
if(IS_PLAYER(lostkey.pusher))
attacker = lostkey.pusher;
- players = keys = 0;
-
if(attacker)
{
if(lostkey.kh_previous_owner)
}
else
{
- float of, fragsleft, i, j, thisteam;
- of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
+ int players = 0;
+ float of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor;
- FOREACH_CLIENT(IS_PLAYER(it) && it.team != teem, LAMBDA(++players));
+ FOREACH_CLIENT(IS_PLAYER(it) && it.team != loser_team, LAMBDA(++players));
+ entity key;
+ int keys = 0;
FOR_EACH_KH_KEY(key)
- if(key.owner && key.team != teem)
+ if(key.owner && key.team != loser_team)
++keys;
if(lostkey.kh_previous_owner)
DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players);
FOR_EACH_KH_KEY(key)
- if(key.owner && key.team != teem)
+ if(key.owner && key.team != loser_team)
{
f = DistributeEvenly_Get(of);
kh_Scores_Event(key.owner, NULL, "destroyed_holdingkey", f, 0);
}
- fragsleft = DistributeEvenly_Get(players);
+ int fragsleft = DistributeEvenly_Get(players);
// Now distribute these among all other teams...
- j = NumTeams(kh_teams) - 1;
- for(i = 0; i < NumTeams(kh_teams); ++i)
+ int j = NumTeams(kh_teams) - 1;
+ for(int i = 0; i < NumTeams(kh_teams); ++i)
{
- thisteam = kh_Team_ByID(i);
- if(thisteam == teem) // bad boy, no cookie - this WILL happen
+ int thisteam = kh_Team_ByID(i);
+ if(thisteam == loser_team) // bad boy, no cookie - this WILL happen
continue;
players = 0;
void kh_Key_Think(entity this) // runs all the time
{
- if(intermission_running)
+ if(gameover)
return;
if(this.owner)
}
entity key;
- vector p;
- p = this.owner.origin;
+ vector p = this.owner.origin;
FOR_EACH_KH_KEY(key)
if(vdist(key.owner.origin - p, >, autocvar_g_balance_keyhunt_maxdist))
goto not_winning;
void kh_Key_DropOne(entity key)
{
// prevent collecting this one for some time
- entity player;
- player = key.owner;
+ entity player = key.owner;
key.kh_droptime = time;
key.enemy = player;
void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies
{
- entity key;
- entity mypusher;
if(player.kh_next)
{
- mypusher = NULL;
+ entity mypusher = NULL;
if(player.pusher)
if(time < player.pushltime)
mypusher = player.pusher;
+
+ entity key;
while((key = player.kh_next))
{
kh_Scores_Event(player, key, "losekey", 0, 0);
void kh_StartRound() // runs at the start of each round
{
- int i, players, teem;
-
if(time < game_starttime)
{
kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers);
Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT);
Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER);
- for(i = 0; i < NumTeams(kh_teams); ++i)
+ for(int i = 0; i < NumTeams(kh_teams); ++i)
{
- teem = kh_Team_ByID(i);
- players = 0;
+ int teem = kh_Team_ByID(i);
+ int players = 0;
entity my_player = NULL;
FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem)
{
if(attacker.team == targ.team)
{
- entity k;
- float nk;
- nk = 0;
- for(k = targ.kh_next; k != NULL; k = k.kh_next)
+ int nk = 0;
+ for(entity k = targ.kh_next; k != NULL; k = k.kh_next)
++nk;
kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0);
}
if (this.bot_strategytime < time)
{
- float key_owner_team;
-
this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
navigation_goalrating_start(this);
- key_owner_team = kh_Key_AllOwnedByWhichTeam();
+ int key_owner_team = kh_Key_AllOwnedByWhichTeam();
if(key_owner_team == this.team)
havocbot_goalrating_kh(this, 10, 0.1, 0.1); // defend anyway
else if(key_owner_team == -1)
teamsay = false;
}
- if(intermission_running)
+ if(gameover)
teamsay = false;
if (!source) {
if (!privatesay && source && !IS_PLAYER(source))
{
- if (!intermission_running)
- if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover)))
- teamsay = -1; // spectators
+ if (!gameover)
+ if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))
+ teamsay = -1; // spectators
}
if(flood)
ret = 1;
}
+ MUTATOR_CALLHOOK(ChatMessage, source, ret);
+ ret = M_ARGV(1, int);
+
if(sourcemsgstr != "" && ret != 0)
{
if(ret < 0) // faked message, because the player is muted
else if(privatesay) // private message, between 2 people only
{
sprint(source, sourcemsgstr);
- sprint(privatesay, msgstr);
if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
- if(cmsgstr != "")
- centerprint(privatesay, cmsgstr);
+ if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source))
+ {
+ sprint(privatesay, msgstr);
+ if(cmsgstr != "")
+ centerprint(privatesay, cmsgstr);
+ }
}
else if ( teamsay && source.active_minigame )
{
sprint(source, sourcemsgstr);
dedicated_print(msgstr); // send to server console too
- FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr));
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
}
else if(teamsay > 0) // team message, only sent to team mates
{
dedicated_print(msgstr); // send to server console too
if(sourcecmsgstr != "")
centerprint(source, sourcecmsgstr);
- FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, {
+ FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
sprint(it, msgstr);
if(cmsgstr != "")
centerprint(it, cmsgstr);
{
sprint(source, sourcemsgstr);
dedicated_print(msgstr); // send to server console too
- FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
+ FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
}
else
{
dedicated_print(msgstr); // send to server console too
MX_Say(strcat(playername(source), "^7: ", msgin));
}
- FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
}
}
}
if (gameover)
+ gameover = false;
+
+ if (intermission_running)
{
round_handler_Reset(0);
round_handler_Remove();
// schedule a new round
this.wait = true;
this.nextthink = time + this.delay;
+ gameover = true;
}
else
{
void Weapon_whereis(Weapon this, entity cl)
{
if (!autocvar_g_showweaponspawns) return;
- IL_EACH(g_items, it.weapon == this.m_id,
+ IL_EACH(g_items, it.weapon == this.m_id && (it.ItemStatus & ITS_AVAILABLE),
{
if (it.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2)
continue;
void CL_Weaponentity_Think(entity this)
{
this.nextthink = time;
- if (intermission_running) this.frame = this.anim_idle.x;
+ if (gameover) this.frame = this.anim_idle.x;
.entity weaponentity = this.weaponentity_fld;
if (this.owner.(weaponentity) != this)
{