From: Mario Date: Sat, 10 Dec 2016 06:20:22 +0000 (+0000) Subject: Merge branch 'terencehill/ca_bots_fix' into 'master' X-Git-Tag: xonotic-v0.8.2~388 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=c1389c740a4a144e46543e51296ad90ade3c365b;hp=f8d1d29d782183456e036ff96a2f55080d980247 Merge branch 'terencehill/ca_bots_fix' into 'master' CA and LMS: fix for bots getting stuck * CA: fix bots getting stuck right after one of them dies * LMS: fix bots getting stuck right after one of them is out of lives * Fix "bot_cmd X pause" command not always working See merge request !389 --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 70b0200061..63130f8ba1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ test_sv_game: - 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 '^:' diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 8ed6aa1aec..bf35a4189e 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -389,10 +389,10 @@ set bot_ai_navigation_jetpack 0 "Enable bots to navigate maps using the jetpack" 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" diff --git a/notifications.cfg b/notifications.cfg index 8583592b9a..8ca401bb42 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -424,10 +424,11 @@ seta notification_INFO_WEAPON_TUBA_SUICIDE "1" "0 = off, 1 = print to console, 2 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" @@ -886,4 +887,4 @@ seta notification_show_sprees_info "3" "Show spree information in MSG_INFO messa 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 diff --git a/qcsrc/client/defs.qh b/qcsrc/client/defs.qh index 437e1dd75b..309ed1d815 100644 --- a/qcsrc/client/defs.qh +++ b/qcsrc/client/defs.qh @@ -120,7 +120,7 @@ int serverflags; 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 diff --git a/qcsrc/client/hud/panel/timer.qc b/qcsrc/client/hud/panel/timer.qc index fab0392dc0..45f46b4087 100644 --- a/qcsrc/client/hud/panel/timer.qc +++ b/qcsrc/client/hud/panel/timer.qc @@ -50,15 +50,15 @@ void HUD_Timer() } 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 diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 76125e60b7..e46a97cb1d 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -49,34 +49,41 @@ void Draw_ShowNames(entity this) 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 { @@ -87,7 +94,7 @@ void Draw_ShowNames(entity this) 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); } @@ -193,8 +200,6 @@ void Draw_ShowNames_All() 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); diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 2ca531166a..5f88e78edf 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -747,7 +747,7 @@ bool WantEventchase(entity this) { if(autocvar_cl_orthoview) return false; - if(intermission) + if(STAT(GAMEOVER) || intermission) return true; if(this.viewloc) return true; @@ -940,7 +940,7 @@ void HUD_Crosshair(entity this) { 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() ) { @@ -1748,8 +1748,8 @@ void CSQC_UpdateView(entity this, float w, float h) if(!postinit) PostInit(); - if(intermission && !gameover_time) - gameover_time = time; + if(intermission && !intermission_time) + intermission_time = time; if(intermission && !isdemo() && !(calledhooks & HOOK_END)) { diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index da53f68a0f..71052bd2b7 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -73,6 +73,11 @@ MACRO_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; diff --git a/qcsrc/common/ent_cs.qh b/qcsrc/common/ent_cs.qh index ac06dc4788..65cdd83d3a 100644 --- a/qcsrc/common/ent_cs.qh +++ b/qcsrc/common/ent_cs.qh @@ -68,17 +68,6 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) /** * @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) { diff --git a/qcsrc/common/items/item/armor.qh b/qcsrc/common/items/item/armor.qh index 7946fb7b5d..0a52754851 100644 --- a/qcsrc/common/items/item/armor.qh +++ b/qcsrc/common/items/item/armor.qh @@ -91,6 +91,7 @@ REGISTER_ITEM(ArmorMega, Armor) { 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); diff --git a/qcsrc/common/items/item/health.qh b/qcsrc/common/items/item/health.qh index 1597ba6057..3717bf5be4 100644 --- a/qcsrc/common/items/item/health.qh +++ b/qcsrc/common/items/item/health.qh @@ -91,6 +91,7 @@ REGISTER_ITEM(HealthMega, Health) { 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); diff --git a/qcsrc/common/items/item/powerup.qh b/qcsrc/common/items/item/powerup.qh index 26d649d7ad..df9315e2c8 100644 --- a/qcsrc/common/items/item/powerup.qh +++ b/qcsrc/common/items/item/powerup.qh @@ -9,7 +9,7 @@ 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)); diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index 32c61eb6aa..45037186d0 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -231,6 +231,7 @@ void buff_Think(entity this) this.skin = buff.m_skin; setmodel(this, MDL_BUFF); + setsize(this, BUFF_MIN, BUFF_MAX); if(this.buff_waypoint) { diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh index b9fc1e42d2..5b14c39f41 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh @@ -70,8 +70,8 @@ float autocvar_g_buffs_luck_damagemultiplier = 3; .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; diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc index 143b3c6edf..76e735e550 100644 --- a/qcsrc/common/mutators/mutator/nix/sv_nix.qc +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qc @@ -256,7 +256,7 @@ MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) { entity player = M_ARGV(0, entity); - if(!intermission_running) + if(!gameover) if(!IS_DEAD(player)) if(IS_PLAYER(player)) NIX_GiveCurrentWeapon(player); diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc index 21a191a0e6..9b00c44cfc 100644 --- a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc @@ -164,7 +164,7 @@ MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) { - if(intermission_running || gameover) + if(gameover) return; entity player = M_ARGV(0, entity); diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index a6431cf673..92fd5df1ac 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -507,6 +507,7 @@ 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"), "") diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index 82398aa56b..b3459a67b9 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -63,9 +63,10 @@ REGISTER_STAT(WEAPON_NEXTTHINK, float) #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) diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 3f5f404358..95cc74802c 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -391,7 +391,7 @@ bool bumblebee_pilot_frame(entity this, float dt) entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { vehic.solid = SOLID_NOT; vehic.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 0cdfc8cb87..80c28a8ab9 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -151,7 +151,7 @@ bool racer_frame(entity this, float dt) entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { vehic.solid = SOLID_NOT; vehic.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index 1068e7430d..7c433f9c76 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -133,7 +133,7 @@ bool raptor_frame(entity this, float dt) entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { vehic.solid = SOLID_NOT; vehic.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index d99335a51c..6ad46946d7 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -48,7 +48,7 @@ bool spiderbot_frame(entity this, float dt) entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { vehic.solid = SOLID_NOT; vehic.takedamage = DAMAGE_NO; diff --git a/qcsrc/menu/xonotic/credits.qc b/qcsrc/menu/xonotic/credits.qc index d30ab3d447..816120b074 100644 --- a/qcsrc/menu/xonotic/credits.qc +++ b/qcsrc/menu/xonotic/credits.qc @@ -402,7 +402,6 @@ void XonoticCreditsList_resizeNotify(entity me, vector relOrigin, vector relSize } 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; diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc index 85e0e9e698..481914200a 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc @@ -58,6 +58,27 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax, 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; @@ -210,28 +231,12 @@ void XonoticServerCreateTab_fill(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); } diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index 427a229fb0..ed9070f9cd 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -72,6 +72,7 @@ void bot_think(entity this) if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start)) { + this.movement = '0 0 0'; this.bot_nextthink = time + 0.5; return; } @@ -118,6 +119,7 @@ void bot_think(entity this) // 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 @@ -664,7 +666,7 @@ void bot_clear(entity this) void bot_serverframe() { - if (intermission_running) + if (gameover) return; if (time < 2) diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index dd44bbb4e6..e599bb8bd6 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -48,7 +48,7 @@ void havocbot_ai(entity this) // 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)) { diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index 7c71720c4c..5092a65c66 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -1032,7 +1032,7 @@ void navigation_unstuck(entity this) 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; @@ -1044,7 +1044,7 @@ void navigation_unstuck(entity this) 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)) { diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 04c35a1176..9583118cea 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -284,8 +284,8 @@ void PutObserverInServer(entity this) 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) { @@ -2320,8 +2320,9 @@ void PlayerPreThink (entity this) if (IS_PLAYER(this)) { CheckRules_Player(this); - if (intermission_running) { - IntermissionThink(this); + if (gameover || intermission_running) { + if(intermission_running) + IntermissionThink(this); return; } @@ -2472,8 +2473,9 @@ void PlayerPreThink (entity this) 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)) { @@ -2615,13 +2617,23 @@ void PlayerPostThink (entity 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); } diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 85c5d18472..44b8acb8e7 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -465,7 +465,7 @@ void ReadyRestart_force() 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! diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 52153fcd7e..fedd5eb1ab 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -129,7 +129,6 @@ const int W_TICSPERFRAME = 2; void weapon_defaultspawnfunc(entity this, Weapon e); -float gameover; float intermission_running; float intermission_exittime; float alreadychangedlevel; diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index fd725ad35e..cccba42bb4 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -144,7 +144,7 @@ void GrapplingHookThink(entity this) 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; diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 8edcf3b8f5..4ca9ef77c8 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -1308,7 +1308,6 @@ When the player presses attack or jump, change to the next level 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); @@ -1496,9 +1495,6 @@ void FixIntermissionClient(entity e) 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]; @@ -1536,7 +1532,7 @@ void NextLevel() 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 @@ -1843,7 +1839,7 @@ void CheckRules_World() 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(); diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 7f4899f127..d2aff2ae7b 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -461,7 +461,7 @@ void GetCvars(entity this, int f) 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)); diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index b6e8c7f4b7..cab67d0c65 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -891,3 +891,18 @@ MUTATOR_HOOKABLE(ForbidWeaponUse, EV_ForbidWeaponUse); /** 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); diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc index 1a1d7959fd..d92e77158c 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@ -1,6 +1,7 @@ #include "gamemode_assault.qh" .entity sprite; +#define AS_ROUND_DELAY 5 // random functions void assault_objective_use(entity this, entity actor, entity trigger) @@ -202,14 +203,27 @@ void assault_new_round(entity this) }); // 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; @@ -229,7 +243,7 @@ int WinningCondition_Assault() { 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)); @@ -240,7 +254,17 @@ int WinningCondition_Assault() } 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)); } } } diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc index e4918ee14b..2aede92040 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc @@ -47,17 +47,17 @@ bool kh_no_radar_circles; // 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; @@ -69,16 +69,17 @@ float kh_Team_ByID(float t) //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(); @@ -119,10 +120,8 @@ bool kh_Key_waypointsprite_visible_for_player(entity this, entity player, entity 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) @@ -156,10 +155,13 @@ void kh_Controller_SetThink(float t, kh_Think_t func) // runs occasionaly 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; @@ -173,7 +175,7 @@ void kh_Controller_Think(entity this) // called a lot 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) @@ -214,8 +216,7 @@ vector kh_AttachedOrigin(entity e) // runs when a team captures the flag, it ca 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); @@ -258,8 +259,7 @@ void kh_Key_Attach(entity key) // runs when a player picks up a key and several 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) @@ -299,12 +299,10 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs 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) { @@ -376,15 +374,16 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic 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; @@ -399,7 +398,7 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic { 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; @@ -449,7 +448,7 @@ void kh_Key_Collect(entity key, entity player) //a player picks up a dropped ke 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 @@ -475,8 +474,7 @@ void kh_Key_Touch(entity this, entity toucher) // runs many, many times when a 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); @@ -522,27 +520,23 @@ void kh_FinishRound() // runs when a team captures the keys 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) @@ -553,17 +547,13 @@ void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TE 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; @@ -579,27 +569,21 @@ void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TE 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) @@ -611,13 +595,15 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl } 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) @@ -630,20 +616,20 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl 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; @@ -673,7 +659,7 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl void kh_Key_Think(entity this) // runs all the time { - if(intermission_running) + if(gameover) return; if(this.owner) @@ -699,8 +685,7 @@ void kh_Key_Think(entity this) // runs all the time } 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; @@ -808,8 +793,7 @@ int kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the 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; @@ -831,14 +815,14 @@ void kh_Key_DropOne(entity key) 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); @@ -922,8 +906,6 @@ void kh_EnableTrackingDevice() // runs after each round 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); @@ -939,10 +921,10 @@ void kh_StartRound() // runs at the start of each round 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) @@ -969,10 +951,8 @@ float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the play { 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); } @@ -1216,12 +1196,10 @@ void havocbot_role_kh_freelancer(entity this) 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) diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index 9125b50d7c..9cccbe887a 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -699,7 +699,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc teamsay = false; } - if(intermission_running) + if(gameover) teamsay = false; if (!source) { @@ -876,9 +876,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc 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) @@ -909,6 +909,9 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc 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 @@ -920,16 +923,19 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc 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 { @@ -937,7 +943,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc 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); @@ -947,7 +953,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc { 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 { @@ -956,7 +962,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc 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)); } } diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc index 8cc3295b9a..063c12abb0 100644 --- a/qcsrc/server/round_handler.qc +++ b/qcsrc/server/round_handler.qc @@ -13,6 +13,9 @@ void round_handler_Think(entity this) } if (gameover) + gameover = false; + + if (intermission_running) { round_handler_Reset(0); round_handler_Remove(); @@ -56,6 +59,7 @@ void round_handler_Think(entity this) // schedule a new round this.wait = true; this.nextthink = time + this.delay; + gameover = true; } else { diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc index 539371a53c..5df7accc0b 100644 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@ -22,7 +22,7 @@ void Send_WeaponComplain(entity e, float wpn, float type) 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; diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index c0d302e223..7dbdae6838 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -67,7 +67,7 @@ vector CL_Weapon_GetShotOrg(int wpn) 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) {