From: Mario Date: Sat, 20 Oct 2018 17:08:36 +0000 (+1000) Subject: Merge branch 'master' into Mario/qcphysics_tweaks X-Git-Tag: xonotic-v0.8.5~1754^2~2 X-Git-Url: https://git.xonotic.org/?a=commitdiff_plain;h=be8d35c9ec5e173f26db53b4b1a7b5fd0b746a60;hp=3fbe2612073d87265cecd108e860081db95acf9e;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into Mario/qcphysics_tweaks --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed13dd03f..66b075642 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ test_sv_game: - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache - make - - EXPECT=c2ae476e90317feda0756669a21ce1e7 + - EXPECT=538cd6a692f22150ac60d9902eddc75b - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg | tee /dev/stderr | grep '^:' diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg index d2ff12f6b..45a75349b 100644 --- a/bal-wep-mario.cfg +++ b/bal-wep-mario.cfg @@ -60,8 +60,8 @@ set g_balance_shotgun_secondary_alt_animtime 0.2 set g_balance_shotgun_secondary_alt_refire 1.2 set g_balance_shotgun_switchdelay_drop 0.2 set g_balance_shotgun_switchdelay_raise 0.2 -set g_balance_shotgun_weaponreplace "shockwave" -set g_balance_shotgun_weaponstart 0 +set g_balance_shotgun_weaponreplace "" +set g_balance_shotgun_weaponstart 1 set g_balance_shotgun_weaponstartoverride -1 set g_balance_shotgun_weaponthrowable 1 // }}} @@ -260,9 +260,9 @@ set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_damage 50 -set g_balance_crylink_secondary_edgedamage 15 -set g_balance_crylink_secondary_force -400 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -200 set g_balance_crylink_secondary_joindelay 0 set g_balance_crylink_secondary_joinexplode 0 set g_balance_crylink_secondary_joinexplode_damage 0 @@ -270,17 +270,17 @@ set g_balance_crylink_secondary_joinexplode_edgedamage 0 set g_balance_crylink_secondary_joinexplode_force 0 set g_balance_crylink_secondary_joinexplode_radius 0 set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_linkexplode 0 set g_balance_crylink_secondary_middle_fadetime 5 set g_balance_crylink_secondary_middle_lifetime 5 -set g_balance_crylink_secondary_other_fadetime 5 -set g_balance_crylink_secondary_other_lifetime 5 -set g_balance_crylink_secondary_radius 70 -set g_balance_crylink_secondary_refire 0.8 -set g_balance_crylink_secondary_shots 1 -set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0 -set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_secondary_other_fadetime 2 +set g_balance_crylink_secondary_other_lifetime 2 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_refire 0.65 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_speed 7000 +set g_balance_crylink_secondary_spread 0.08 +set g_balance_crylink_secondary_spreadtype 0 set g_balance_crylink_switchdelay_drop 0.2 set g_balance_crylink_switchdelay_raise 0.2 set g_balance_crylink_weaponreplace "" @@ -725,7 +725,7 @@ set g_balance_shockwave_melee_traces 10 set g_balance_shockwave_switchdelay_drop 0.2 set g_balance_shockwave_switchdelay_raise 0.2 set g_balance_shockwave_weaponreplace "" -set g_balance_shockwave_weaponstart 1 +set g_balance_shockwave_weaponstart 0 set g_balance_shockwave_weaponstartoverride -1 set g_balance_shockwave_weaponthrowable 0 // }}} @@ -753,10 +753,10 @@ set g_balance_arc_beam_heat 0 set g_balance_arc_burst_heat 5 set g_balance_arc_beam_maxangle 10 set g_balance_arc_beam_nonplayerdamage 80 -set g_balance_arc_beam_range 1250 +set g_balance_arc_beam_range 1500 set g_balance_arc_beam_refire 0.25 set g_balance_arc_beam_returnspeed 8 -set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_beam_tightness 0.6 set g_balance_arc_bolt 1 set g_balance_arc_bolt_ammo 1 set g_balance_arc_bolt_damage 25 @@ -883,7 +883,7 @@ set g_balance_okmachinegun_primary_ammo 1 set g_balance_okmachinegun_primary_damage 25 set g_balance_okmachinegun_primary_force 5 set g_balance_okmachinegun_primary_refire 0.1 -set g_balance_okmachinegun_primary_solidpenetration 13.1 +set g_balance_okmachinegun_primary_solidpenetration 63 set g_balance_okmachinegun_primary_spread_add 0.012 set g_balance_okmachinegun_primary_spread_max 0.05 set g_balance_okmachinegun_primary_spread_min 0 diff --git a/gfx/menu/luma/icon_ipv6.tga b/gfx/menu/luma/icon_ipv6.tga index a41f9ff92..db474afe7 100644 Binary files a/gfx/menu/luma/icon_ipv6.tga and b/gfx/menu/luma/icon_ipv6.tga differ diff --git a/gfx/menu/luma/icon_mod_xpm.tga b/gfx/menu/luma/icon_mod_xpm.tga new file mode 100644 index 000000000..47c3fb641 Binary files /dev/null and b/gfx/menu/luma/icon_mod_xpm.tga differ diff --git a/gfx/menu/luminos/icon_mod_xpm.tga b/gfx/menu/luminos/icon_mod_xpm.tga new file mode 100644 index 000000000..3d51ff3be Binary files /dev/null and b/gfx/menu/luminos/icon_mod_xpm.tga differ diff --git a/gfx/menu/wickedx/icon_mod_xpm.tga b/gfx/menu/wickedx/icon_mod_xpm.tga new file mode 100644 index 000000000..3d51ff3be Binary files /dev/null and b/gfx/menu/wickedx/icon_mod_xpm.tga differ diff --git a/gfx/menu/xaw/icon_mod_xpm.tga b/gfx/menu/xaw/icon_mod_xpm.tga new file mode 100644 index 000000000..3d51ff3be Binary files /dev/null and b/gfx/menu/xaw/icon_mod_xpm.tga differ diff --git a/qcsrc/client/hud/panel/infomessages.qc b/qcsrc/client/hud/panel/infomessages.qc index 7b7d82b44..f63ffb1de 100644 --- a/qcsrc/client/hud/panel/infomessages.qc +++ b/qcsrc/client/hud/panel/infomessages.qc @@ -118,7 +118,7 @@ void HUD_InfoMessages() MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize); - if(!warmup_stage && gametype == MAPINFO_TYPE_LMS) + if(!warmup_stage && ISGAMETYPE(LMS)) { entity sk; sk = playerslots[player_localnum]; diff --git a/qcsrc/client/hud/panel/modicons.qc b/qcsrc/client/hud/panel/modicons.qc index 87e4a7fb2..4d1691a7f 100644 --- a/qcsrc/client/hud/panel/modicons.qc +++ b/qcsrc/client/hud/panel/modicons.qc @@ -54,9 +54,9 @@ void HUD_Mod_CA(vector myPos, vector mySize) mod_active = 1; // required in each mod function that always shows something int layout; - if(gametype == MAPINFO_TYPE_CA) + if(ISGAMETYPE(CA)) layout = autocvar_hud_panel_modicons_ca_layout; - else //if(gametype == MAPINFO_TYPE_FREEZETAG) + else //if(ISGAMETYPE(FREEZETAG)) layout = autocvar_hud_panel_modicons_freezetag_layout; int rows, columns; float aspect_ratio; @@ -528,7 +528,7 @@ void HUD_Mod_Race(vector pos, vector mySize) // clientside personal record string rr; - if(gametype == MAPINFO_TYPE_CTS) + if(ISGAMETYPE(CTS)) rr = CTS_RECORD; else rr = RACE_RECORD; diff --git a/qcsrc/client/hud/panel/physics.qc b/qcsrc/client/hud/panel/physics.qc index a6c65183d..aa77690a6 100644 --- a/qcsrc/client/hud/panel/physics.qc +++ b/qcsrc/client/hud/panel/physics.qc @@ -18,7 +18,7 @@ void HUD_Physics() { if(!autocvar_hud_panel_physics) return; if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return; - if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(autocvar_hud_panel_physics == 3 && !(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return; } HUD_Panel_LoadCvars(); diff --git a/qcsrc/client/hud/panel/racetimer.qc b/qcsrc/client/hud/panel/racetimer.qc index 6a190f2ca..7d09cf1ff 100644 --- a/qcsrc/client/hud/panel/racetimer.qc +++ b/qcsrc/client/hud/panel/racetimer.qc @@ -98,7 +98,7 @@ void HUD_RaceTimer () if(!autocvar__hud_configure) { if(!autocvar_hud_panel_racetimer) return; - if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(!(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return; if(spectatee_status == -1) return; } diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc index 9176aa0ff..cd4551725 100644 --- a/qcsrc/client/hud/panel/radar.qc +++ b/qcsrc/client/hud/panel/radar.qc @@ -343,7 +343,7 @@ void HUD_Radar() IL_EACH(g_radaricons, it.teamradar_icon, { if ( hud_panel_radar_mouse ) if ( GetResourceAmount(it, RESOURCE_HEALTH) >= 0 ) - if ( it.team == myteam + 1 || gametype == MAPINFO_TYPE_RACE || !teamplay ) + if ( it.team == myteam + 1 || ISGAMETYPE(RACE) || !teamplay ) { vector coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(it.origin)); if(vdist((mousepos - coord), <, 8)) diff --git a/qcsrc/client/hud/panel/score.qc b/qcsrc/client/hud/panel/score.qc index 56fa5867c..525bf614b 100644 --- a/qcsrc/client/hud/panel/score.qc +++ b/qcsrc/client/hud/panel/score.qc @@ -140,7 +140,7 @@ void HUD_Score() if(!autocvar__hud_configure) { if(!autocvar_hud_panel_score) return; - if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(spectatee_status == -1 && (ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return; } HUD_Panel_LoadCvars(); diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc index 3d9e333ba..4989aac50 100644 --- a/qcsrc/client/hud/panel/scoreboard.qc +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -1145,7 +1145,7 @@ bool Scoreboard_WouldDraw() return true; else if (intermission == 2) return false; - else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame) + else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !ISGAMETYPE(CTS) && !active_minigame) return true; else if (scoreboard_showscores_force) return true; @@ -1404,7 +1404,7 @@ vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_siz vector hl_rgb = rgb + '0.5 0.5 0.5'; pos.y += hud_fontsize.y; - drawstring(pos + eX * panel_bg_padding, ((gametype == MAPINFO_TYPE_CTF) ? _("Capture time rankings") : _("Rankings")), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(pos + eX * panel_bg_padding, ((ISGAMETYPE(CTF)) ? _("Capture time rankings") : _("Rankings")), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += 1.25 * hud_fontsize.y; if(panel.current_panel_bg != "0") pos.y += panel_bg_border; @@ -1498,7 +1498,7 @@ float scoreboard_time; bool have_weapon_stats; bool Scoreboard_AccuracyStats_WouldDraw(float ypos) { - if (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_NEXBALL) + if (ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || ISGAMETYPE(NEXBALL)) return false; if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight) return false; @@ -1666,7 +1666,7 @@ void Scoreboard_Draw() if (Scoreboard_AccuracyStats_WouldDraw(pos.y)) pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size); - if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (autocvar_hud_panel_scoreboard_ctf_leaderboard && gametype == MAPINFO_TYPE_CTF && STAT(CTF_SHOWLEADERBOARD))) { + if(ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || (autocvar_hud_panel_scoreboard_ctf_leaderboard && ISGAMETYPE(CTF) && STAT(CTF_SHOWLEADERBOARD))) { if(race_speedaward) { drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, ColorTranslateRGB(race_speedaward_holder)), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += 1.25 * hud_fontsize.y; @@ -1707,7 +1707,7 @@ void Scoreboard_Draw() tl = STAT(TIMELIMIT); fl = STAT(FRAGLIMIT); ll = STAT(LEADLIMIT); - if(gametype == MAPINFO_TYPE_LMS) + if(ISGAMETYPE(LMS)) { if(tl > 0) str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl)); diff --git a/qcsrc/client/hud/panel/vote.qc b/qcsrc/client/hud/panel/vote.qc index 57b32039d..0337eccfc 100644 --- a/qcsrc/client/hud/panel/vote.qc +++ b/qcsrc/client/hud/panel/vote.qc @@ -9,7 +9,7 @@ void HUD_Vote() { - if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS))) + if(autocvar_cl_allow_uid2name == -1 && (ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || (serverflags & SERVERFLAG_PLAYERSTATS))) { // this dialog gets overriden by the uid2name menu dialog, if it exists // TODO remove this client side uid2name dialog in the next release diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index 8a0e78a0d..f0f8f1d4b 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -11,6 +11,8 @@ string minimapname; bool postinit; entity gametype; +// temporary hack +#define ISGAMETYPE(NAME) (gametype == MAPINFO_TYPE_##NAME) float FONT_USER = 8; diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index eac36c1ae..2fc155949 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -128,7 +128,7 @@ void Draw_ShowNames(entity this) // FIXME: alpha is negative when dead, breaking death fade if (!this.csqcmodel_isdead) a *= f; } - if (a < ALPHA_MIN_VISIBLE && gametype != MAPINFO_TYPE_CTS) return; + if (a < ALPHA_MIN_VISIBLE && ISGAMETYPE(CTS)) return; if (vdist(this.origin - view_origin, >=, max_shot_distance)) return; float dist = vlen(this.origin - view_origin); if (autocvar_hud_shownames_maxdistance) diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index b72998de6..8fba190c6 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -1539,7 +1539,7 @@ void HUD_Draw(entity this) if(autocvar_r_letterbox == 0) if(autocvar_viewsize < 120) { - if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) + if(!(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) Accuracy_LoadLevels(); HUD_Main(); @@ -2467,7 +2467,7 @@ void CSQC_UpdateView(entity this, float w, float h) else if(cvar("r_glsl_postprocess") == 2) cvar_set("r_glsl_postprocess", "0"); - /*if(gametype == MAPINFO_TYPE_CTF) + /*if(ISGAMETYPE(CTF)) { ctf_view(); } else */ diff --git a/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh index 95b5b8ccf..fcfc78917 100644 --- a/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh +++ b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh @@ -41,5 +41,8 @@ void(entity bot) havocbot_ast_reset_role; void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items; void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; +// assault game mode: Which team is attacking in this round? +float assault_attacker_team; + // predefined spawnfuncs void target_objective_decrease_activate(entity this); diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 89c53a82a..65c5bcd8a 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -7,7 +7,7 @@ REGISTER_MUTATOR(cl_nb, true); MUTATOR_HOOKFUNCTION(cl_nb, WantEventchase) { - if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WEPSET(NEXBALL))) + if(autocvar_cl_eventchase_nexball && ISGAMETYPE(NEXBALL) && !(WepSet_GetFromStat() & WEPSET(NEXBALL))) return true; return false; } diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 17093f88e..5cfc87851 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -424,27 +424,7 @@ void _MapInfo_Map_Reset() string _MapInfo_GetDefault(Gametype t) { - switch(t) - { - case MAPINFO_TYPE_DEATHMATCH: return "30 20 0"; - case MAPINFO_TYPE_TEAM_DEATHMATCH: return "50 20 2 0"; - case MAPINFO_TYPE_DOMINATION: return "200 20 0"; - case MAPINFO_TYPE_CTF: return "300 20 10 0"; - case MAPINFO_TYPE_LMS: return "9 20 0"; - case MAPINFO_TYPE_CA: return "10 20 0"; - case MAPINFO_TYPE_KEYHUNT: return "1000 20 3 0"; - case MAPINFO_TYPE_ASSAULT: return "20 0"; - case MAPINFO_TYPE_RACE: return "20 5 7 15 0"; - case MAPINFO_TYPE_ONSLAUGHT: return "20 0"; - case MAPINFO_TYPE_NEXBALL: return "5 20 0"; - case MAPINFO_TYPE_CTS: return "20 0 0"; - case MAPINFO_TYPE_FREEZETAG: return "10 20 0"; - // NOTE: DO NOT ADD ANY MORE GAME TYPES HERE - // THIS IS JUST LEGACY SUPPORT FOR NEXUIZ MAPS - // ONLY ADD NEW STUFF TO _MapInfo_GetDefaultEx - // THIS FUNCTION WILL EVENTUALLY BE REMOVED - default: return ""; - } + return t.m_legacydefaults; } void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisType, int load_default) @@ -457,7 +437,7 @@ void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisTy if(load_default) _MapInfo_Map_ApplyGametype(_MapInfo_GetDefault(pThisType), pWantedType, pThisType, false); - if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_RACE || pWantedType == MAPINFO_TYPE_CTS) // these modes don't use fraglimit + if(!pWantedType.frags) // these modes don't use fraglimit { cvar_set("fraglimit", "0"); } @@ -485,6 +465,8 @@ void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisTy // rc = timelimit timelimit_qualification laps laps_teamplay if(pWantedType == MAPINFO_TYPE_RACE) { + cvar_set("fraglimit", "0"); // special case! + sa = car(s); if(sa == "") sa = cvar_string("timelimit"); cvar_set("g_race_qualifying_timelimit", sa); s = cdr(s); @@ -502,7 +484,7 @@ void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisTy s = cdr(s); } - if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_CTS) // these modes don't use fraglimit + if(!pWantedType.frags) // these modes don't use fraglimit { cvar_set("leadlimit", "0"); } diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 4ece92156..6fbb7ce89 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -31,6 +31,8 @@ CLASS(Gametype, Object) ATTRIB(Gametype, message, string); /** does this gametype support teamplay? */ ATTRIB(Gametype, team, bool, false); + /** does this gametype use a point limit? */ + ATTRIB(Gametype, frags, bool, true); /** game type defaults */ ATTRIB(Gametype, model2, string); /** game type description */ @@ -40,6 +42,9 @@ CLASS(Gametype, Object) ATTRIB(Gametype, m_modicons_reset, void()); #endif + /** DO NOT USE, this is compatibility for legacy maps! */ + ATTRIB(Gametype, m_legacydefaults, string, ""); + ATTRIB(Gametype, m_mutators, string); METHOD(Gametype, m_parse_mapinfo, bool(string k, string v)) { @@ -61,6 +66,11 @@ CLASS(Gametype, Object) { return false; } + METHOD(Gametype, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "fraglimit_override", string_null, _("The amount of frags needed before the match will end")); + } METHOD(Gametype, describe, string(Gametype this)) { @@ -74,7 +84,7 @@ CLASS(Gametype, Object) returns(this.message, strcat("gametype_", this.mdl)); } - METHOD(Gametype, gametype_init, void(Gametype this, string hname, string sname, string g_name, bool gteamplay, string mutators, string defaults, string gdescription)) + METHOD(Gametype, gametype_init, void(Gametype this, string hname, string sname, string g_name, bool gteamplay, bool gusepoints, string mutators, string defaults, string gdescription)) { this.netname = g_name; this.mdl = sname; @@ -83,6 +93,7 @@ CLASS(Gametype, Object) this.m_mutators = cons(sname, mutators); this.model2 = defaults; this.gametype_description = gdescription; + this.frags = gusepoints; // same as `1 << m_id` MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1); @@ -100,24 +111,31 @@ REGISTRY_CHECK(Gametypes) CLASS(Deathmatch, Gametype) INIT(Deathmatch) { - this.gametype_init(this, _("Deathmatch"),"dm","g_dm",false,"","timelimit=15 pointlimit=30 leadlimit=0",_("Score as many frags as you can")); + this.gametype_init(this, _("Deathmatch"),"dm","g_dm",false,true,"","timelimit=15 pointlimit=30 leadlimit=0",_("Score as many frags as you can")); } METHOD(Deathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { return true; } + ATTRIB(Deathmatch, m_legacydefaults, string, "30 20 0"); ENDCLASS(Deathmatch) REGISTER_GAMETYPE(DEATHMATCH, NEW(Deathmatch)); CLASS(LastManStanding, Gametype) INIT(LastManStanding) { - this.gametype_init(this, _("Last Man Standing"),"lms","g_lms",false,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); + this.gametype_init(this, _("Last Man Standing"),"lms","g_lms",false,true,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); } METHOD(LastManStanding, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { return true; } + METHOD(LastManStanding, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Lives:"), 3, 50, 1, "g_lms_lives_override", string_null, string_null); + } + ATTRIB(LastManStanding, m_legacydefaults, string, "9 20 0"); ENDCLASS(LastManStanding) REGISTER_GAMETYPE(LMS, NEW(LastManStanding)); @@ -127,7 +145,7 @@ void HUD_Mod_Race(vector pos, vector mySize); CLASS(Race, Gametype) INIT(Race) { - this.gametype_init(this, _("Race"),"rc","g_race",false,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); + this.gametype_init(this, _("Race"),"rc","g_race",false,true,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); } METHOD(Race, m_parse_mapinfo, bool(string k, string v)) { @@ -151,9 +169,15 @@ CLASS(Race, Gametype) { return true; } + METHOD(Race, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Laps:"), 1, 25, 1, "g_race_laps_limit", string_null, string_null); + } #ifdef CSQC ATTRIB(Race, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); #endif + ATTRIB(Race, m_legacydefaults, string, "20 5 7 15 0"); ENDCLASS(Race) REGISTER_GAMETYPE(RACE, NEW(Race)); #define g_race IS_GAMETYPE(RACE) @@ -161,7 +185,7 @@ REGISTER_GAMETYPE(RACE, NEW(Race)); CLASS(RaceCTS, Gametype) INIT(RaceCTS) { - this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,"cloaked","timelimit=20",_("Race for fastest time.")); + this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,false,"cloaked","timelimit=20",_("Race for fastest time.")); } METHOD(RaceCTS, m_generate_mapinfo, void(Gametype this, string v)) { @@ -175,9 +199,15 @@ CLASS(RaceCTS, Gametype) // for map databases // cvar_set("fraglimit", sa); } + METHOD(RaceCTS, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } #ifdef CSQC ATTRIB(RaceCTS, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); #endif + ATTRIB(RaceCTS, m_legacydefaults, string, "20 0 0"); ENDCLASS(RaceCTS) REGISTER_GAMETYPE(CTS, NEW(RaceCTS)); #define g_cts IS_GAMETYPE(CTS) @@ -185,7 +215,7 @@ REGISTER_GAMETYPE(CTS, NEW(RaceCTS)); CLASS(TeamDeathmatch, Gametype) INIT(TeamDeathmatch) { - this.gametype_init(this, _("Team Deathmatch"),"tdm","g_tdm",true,"","timelimit=15 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team")); + this.gametype_init(this, _("Team Deathmatch"),"tdm","g_tdm",true,true,"","timelimit=15 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team")); } METHOD(TeamDeathmatch, m_parse_mapinfo, bool(string k, string v)) { @@ -220,6 +250,12 @@ CLASS(TeamDeathmatch, Gametype) { cvar_set("g_tdm_teams", sa); } + METHOD(TeamDeathmatch, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 5, 100, 5, "g_tdm_point_limit", "g_tdm_teams_override", _("The amount of points needed before the match will end")); + } + ATTRIB(TeamDeathmatch, m_legacydefaults, string, "50 20 2 0"); ENDCLASS(TeamDeathmatch) REGISTER_GAMETYPE(TEAM_DEATHMATCH, NEW(TeamDeathmatch)); #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH) @@ -231,7 +267,7 @@ void HUD_Mod_CTF_Reset(); CLASS(CaptureTheFlag, Gametype) INIT(CaptureTheFlag) { - this.gametype_init(this, _("Capture the Flag"),"ctf","g_ctf",true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team")); + this.gametype_init(this, _("Capture the Flag"),"ctf","g_ctf",true,true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team")); } METHOD(CaptureTheFlag, m_generate_mapinfo, void(Gametype this, string v)) { @@ -246,10 +282,16 @@ CLASS(CaptureTheFlag, Gametype) { cvar_set("fraglimit", sa); } + METHOD(CaptureTheFlag, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Capture limit:"), 1, 20, 1, "capturelimit_override", string_null, _("The amount of captures needed before the match will end")); + } #ifdef CSQC ATTRIB(CaptureTheFlag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CTF); ATTRIB(CaptureTheFlag, m_modicons_reset, void(), HUD_Mod_CTF_Reset); #endif + ATTRIB(CaptureTheFlag, m_legacydefaults, string, "300 20 10 0"); ENDCLASS(CaptureTheFlag) REGISTER_GAMETYPE(CTF, NEW(CaptureTheFlag)); #define g_ctf IS_GAMETYPE(CTF) @@ -260,7 +302,7 @@ void HUD_Mod_CA(vector pos, vector mySize); CLASS(ClanArena, Gametype) INIT(ClanArena) { - this.gametype_init(this, _("Clan Arena"),"ca","g_ca",true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); + this.gametype_init(this, _("Clan Arena"),"ca","g_ca",true,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); } METHOD(ClanArena, m_parse_mapinfo, bool(string k, string v)) { @@ -285,9 +327,15 @@ CLASS(ClanArena, Gametype) { cvar_set("g_ca_teams", sa); } + METHOD(ClanArena, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_ca_teams_override", _("The amount of frags needed before the match will end")); + } #ifdef CSQC ATTRIB(ClanArena, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA); #endif + ATTRIB(ClanArena, m_legacydefaults, string, "10 20 0"); ENDCLASS(ClanArena) REGISTER_GAMETYPE(CA, NEW(ClanArena)); #define g_ca IS_GAMETYPE(CA) @@ -298,7 +346,7 @@ void HUD_Mod_Dom(vector pos, vector mySize); CLASS(Domination, Gametype) INIT(Domination) { - this.gametype_init(this, _("Domination"),"dom","g_domination",true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win")); + this.gametype_init(this, _("Domination"),"dom","g_domination",true,true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win")); } METHOD(Domination, m_parse_mapinfo, bool(string k, string v)) { @@ -318,9 +366,15 @@ CLASS(Domination, Gametype) if(v == "dom_controlpoint") MapInfo_Map_supportedGametypes |= this.m_flags; } + METHOD(Domination, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, "g_domination_point_limit", "g_domination_teams_override", _("The amount of points needed before the match will end")); + } #ifdef CSQC ATTRIB(Domination, m_modicons, void(vector pos, vector mySize), HUD_Mod_Dom); #endif + ATTRIB(Domination, m_legacydefaults, string, "200 20 0"); ENDCLASS(Domination) REGISTER_GAMETYPE(DOMINATION, NEW(Domination)); @@ -330,7 +384,7 @@ void HUD_Mod_KH(vector pos, vector mySize); CLASS(KeyHunt, Gametype) INIT(KeyHunt) { - this.gametype_init(this, _("Key Hunt"),"kh","g_keyhunt",true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")); + this.gametype_init(this, _("Key Hunt"),"kh","g_keyhunt",true,true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")); } METHOD(KeyHunt, m_parse_mapinfo, bool(string k, string v)) { @@ -355,16 +409,22 @@ CLASS(KeyHunt, Gametype) { cvar_set("g_keyhunt_teams", sa); } + METHOD(KeyHunt, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 200, 1500, 50, "g_keyhunt_point_limit", "g_keyhunt_teams_override", _("The amount of points needed before the match will end")); + } #ifdef CSQC ATTRIB(KeyHunt, m_modicons, void(vector pos, vector mySize), HUD_Mod_KH); #endif + ATTRIB(KeyHunt, m_legacydefaults, string, "1000 20 3 0"); ENDCLASS(KeyHunt) REGISTER_GAMETYPE(KEYHUNT, NEW(KeyHunt)); CLASS(Assault, Gametype) INIT(Assault) { - this.gametype_init(this, _("Assault"),"as","g_assault",true,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); + this.gametype_init(this, _("Assault"),"as","g_assault",true,false,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); } METHOD(Assault, m_generate_mapinfo, void(Gametype this, string v)) { @@ -375,6 +435,12 @@ CLASS(Assault, Gametype) { return true; } + METHOD(Assault, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } + ATTRIB(Assault, m_legacydefaults, string, "20 0"); ENDCLASS(Assault) REGISTER_GAMETYPE(ASSAULT, NEW(Assault)); #define g_assault IS_GAMETYPE(ASSAULT) @@ -382,13 +448,19 @@ REGISTER_GAMETYPE(ASSAULT, NEW(Assault)); CLASS(Onslaught, Gametype) INIT(Onslaught) { - this.gametype_init(this, _("Onslaught"),"ons","g_onslaught",true,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator")); + this.gametype_init(this, _("Onslaught"),"ons","g_onslaught",true,false,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator")); } METHOD(Onslaught, m_generate_mapinfo, void(Gametype this, string v)) { if(v == "onslaught_generator") MapInfo_Map_supportedGametypes |= this.m_flags; } + METHOD(Onslaught, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } + ATTRIB(Onslaught, m_legacydefaults, string, "20 0"); ENDCLASS(Onslaught) REGISTER_GAMETYPE(ONSLAUGHT, NEW(Onslaught)); @@ -398,7 +470,7 @@ void HUD_Mod_NexBall(vector pos, vector mySize); CLASS(NexBall, Gametype) INIT(NexBall) { - this.gametype_init(this, _("Nexball"),"nb","g_nexball",true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean")); + this.gametype_init(this, _("Nexball"),"nb","g_nexball",true,true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean")); } METHOD(NexBall, m_generate_mapinfo, void(Gametype this, string v)) { @@ -409,9 +481,15 @@ CLASS(NexBall, Gametype) { return true; } + METHOD(NexBall, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Goals:"), 1, 50, 1, "g_nexball_goallimit", string_null, _("The amount of goals needed before the match will end")); + } #ifdef CSQC ATTRIB(NexBall, m_modicons, void(vector pos, vector mySize), HUD_Mod_NexBall); #endif + ATTRIB(NexBall, m_legacydefaults, string, "5 20 0"); ENDCLASS(NexBall) REGISTER_GAMETYPE(NEXBALL, NEW(NexBall)); #define g_nexball IS_GAMETYPE(NEXBALL) @@ -419,7 +497,7 @@ REGISTER_GAMETYPE(NEXBALL, NEW(NexBall)); CLASS(FreezeTag, Gametype) INIT(FreezeTag) { - this.gametype_init(this, _("Freeze Tag"),"ft","g_freezetag",true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to frozen teammates to revive them; freeze all enemies to win")); + this.gametype_init(this, _("Freeze Tag"),"ft","g_freezetag",true,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to frozen teammates to revive them; freeze all enemies to win")); } METHOD(FreezeTag, m_parse_mapinfo, bool(string k, string v)) { @@ -444,9 +522,15 @@ CLASS(FreezeTag, Gametype) { cvar_set("g_freezetag_teams", sa); } + METHOD(FreezeTag, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_freezetag_teams_override", _("The amount of frags needed before the match will end")); + } #ifdef CSQC ATTRIB(FreezeTag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA); #endif + ATTRIB(FreezeTag, m_legacydefaults, string, "10 20 0"); ENDCLASS(FreezeTag) REGISTER_GAMETYPE(FREEZETAG, NEW(FreezeTag)); #define g_freezetag IS_GAMETYPE(FREEZETAG) @@ -457,7 +541,7 @@ void HUD_Mod_Keepaway(vector pos, vector mySize); CLASS(Keepaway, Gametype) INIT(Keepaway) { - this.gametype_init(this, _("Keepaway"),"ka","g_keepaway",false,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); + this.gametype_init(this, _("Keepaway"),"ka","g_keepaway",false,true,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); } METHOD(Keepaway, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { @@ -472,7 +556,7 @@ REGISTER_GAMETYPE(KEEPAWAY, NEW(Keepaway)); CLASS(Invasion, Gametype) INIT(Invasion) { - this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,"","pointlimit=50 teams=0 type=0",_("Survive against waves of monsters")); + this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,true,"","pointlimit=50 teams=0 type=0",_("Survive against waves of monsters")); } METHOD(Invasion, m_parse_mapinfo, bool(string k, string v)) { @@ -491,13 +575,18 @@ CLASS(Invasion, Gametype) if(v == "invasion_spawnpoint") MapInfo_Map_supportedGametypes |= this.m_flags; } + METHOD(Invasion, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } ENDCLASS(Invasion) REGISTER_GAMETYPE(INVASION, NEW(Invasion)); CLASS(Duel, Gametype) INIT(Duel) { - this.gametype_init(this, _("Duel"),"duel","g_duel",false,"","timelimit=10 pointlimit=0 leadlimit=0",_("Fight in a one versus one arena battle to decide the winner")); + this.gametype_init(this, _("Duel"),"duel","g_duel",false,true,"","timelimit=10 pointlimit=0 leadlimit=0",_("Fight in a one versus one arena battle to decide the winner")); } METHOD(Duel, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { @@ -585,4 +674,4 @@ void MapInfo_ClearTemps(); // call this when done with mapinfo for this frame void MapInfo_Shutdown(); // call this in the shutdown handler #define MAPINFO_SETTEMP_ACL_USER cvar_string("g_mapinfo_settemp_acl") -#define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* +*" +#define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* -r_water +*" diff --git a/qcsrc/common/mutators/mutator/overkill/okhmg.qc b/qcsrc/common/mutators/mutator/overkill/okhmg.qc index 003cd3abc..688928ce1 100644 --- a/qcsrc/common/mutators/mutator/overkill/okhmg.qc +++ b/qcsrc/common/mutators/mutator/overkill/okhmg.qc @@ -51,8 +51,7 @@ void W_OverkillHeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); } - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(okhmg, refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(okhmg, refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okhmg, refire), W_OverkillHeavyMachineGun_Attack_Auto); } diff --git a/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc b/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc index 27502a759..aa872f154 100644 --- a/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc +++ b/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc @@ -45,8 +45,7 @@ void W_OverkillMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weap SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); } - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(okmachinegun, refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(okmachinegun, refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okmachinegun, refire), W_OverkillMachineGun_Attack_Auto); } diff --git a/qcsrc/common/sounds/all.qc b/qcsrc/common/sounds/all.qc index 328486d35..fcd4d9a45 100644 --- a/qcsrc/common/sounds/all.qc +++ b/qcsrc/common/sounds/all.qc @@ -8,6 +8,7 @@ bool autocvar_bot_sound_monopoly; .entity realowner; bool sound_allowed(int to, entity e) { + if(!e) return true; // save on a few checks for ( ; ; ) { if (e.classname == "body") e = e.enemy; diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 183302b3a..a7e9c4210 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -1298,6 +1298,7 @@ float matchacl(string acl, string str) if(s == t) { r = d; + break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught } } return r; diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index 35bb1e3db..ef4e3eb7d 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -539,17 +539,33 @@ void W_Arc_Beam(float burst, entity actor, .entity weaponentity) getthink(beam)(beam); } -void Arc_Smoke(entity actor, .entity weaponentity) +void W_Arc_Attack(Weapon thiswep, entity actor, .entity weaponentity, int fire) { + if(!actor.(weaponentity).arc_beam || wasfreed(actor.(weaponentity).arc_beam)) + { + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + // attack handled by the beam itself, this is just a loop to keep the attack happening! + + // NOTE: arc doesn't use a refire + //ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(arc, refire) * W_WeaponRateFactor(actor); + actor.(weaponentity).wframe = WFRAME_FIRE1; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), W_Arc_Attack); +} +void Arc_Smoke(Weapon thiswep, entity actor, .entity weaponentity, int fire) +{ + // TODO: spamming this without checking any refires is asking for trouble! makevectors(actor.v_angle); - W_SetupShot_Range(actor,weaponentity,false,0,SND_Null,0,0,0,WEP_ARC.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect + W_SetupShot_Range(actor,weaponentity,false,0,SND_Null,0,0,0,thiswep.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect vector smoke_origin = w_shotorg + actor.velocity*frametime; if ( actor.arc_overheat > time ) { if ( random() < actor.(weaponentity).arc_heat_percent ) Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 ); - if ( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) + if ( (fire & 1) || (fire & 2) ) { Send_Effect(EFFECT_ARC_OVERHEAT_FIRE, smoke_origin, w_shotdir, 1 ); if ( !actor.arc_smoke_sound ) @@ -568,7 +584,7 @@ void Arc_Smoke(entity actor, .entity weaponentity) } if ( actor.arc_smoke_sound && ( actor.arc_overheat <= time || - !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != WEP_ARC ) + !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != thiswep ) { actor.arc_smoke_sound = 0; sound(actor, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); @@ -603,14 +619,14 @@ METHOD(Arc, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { Arc_Player_SetHeat(actor, weaponentity); - Arc_Smoke(actor, weaponentity); + Arc_Smoke(thiswep, actor, weaponentity, fire); bool beam_fire2 = ((fire & 2) && !WEP_CVAR(arc, bolt)); if (time >= actor.arc_overheat) if ((fire & 1) || beam_fire2 || actor.(weaponentity).arc_beam.beam_bursting) { - + #if 0 if(actor.(weaponentity).arc_BUTTON_ATCK_prev) { #if 0 @@ -620,6 +636,7 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i #endif weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), w_ready); } + #endif if((!actor.(weaponentity).arc_beam) || wasfreed(actor.(weaponentity).arc_beam)) { @@ -629,7 +646,8 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i if(!actor.(weaponentity).arc_BUTTON_ATCK_prev) { - weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + actor.(weaponentity).wframe = WFRAME_FIRE1; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), W_Arc_Attack); actor.(weaponentity).arc_BUTTON_ATCK_prev = true; } } @@ -648,10 +666,9 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i if(actor.(weaponentity).arc_BUTTON_ATCK_prev) { - int slot = weaponslot(weaponentity); sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor); } actor.(weaponentity).arc_BUTTON_ATCK_prev = false; diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index 1357fc5f2..f8539b14c 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -50,8 +50,7 @@ void W_Devastator_Explode(entity this, entity directhitentity) if(!(this.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) { this.realowner.cnt = thiswep.m_id; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(this.realowner, slot) = time; + ATTACK_FINISHED(this.realowner, weaponentity) = time; this.realowner.(weaponentity).m_switchweapon = w_getbestweapon(this.realowner, weaponentity); } } @@ -144,8 +143,7 @@ void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity) if(!(this.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) { this.realowner.cnt = thiswep.m_id; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(this.realowner, slot) = time; + ATTACK_FINISHED(this.realowner, weaponentity) = time; this.realowner.(weaponentity).m_switchweapon = w_getbestweapon(this.realowner, weaponentity); } } @@ -488,7 +486,7 @@ METHOD(Devastator, wr_checkammo1, bool(entity thiswep, entity actor, .entity wea { #if 0 // don't switch while guiding a missile - if(ATTACK_FINISHED(actor, slot) <= time || PS(actor).m_weapon != WEP_DEVASTATOR) + if(ATTACK_FINISHED(actor, weaponentity) <= time || PS(actor).m_weapon != WEP_DEVASTATOR) { ammo_amount = false; if(WEP_CVAR(devastator, reload_ammo)) diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc index 4d13e9cb7..58be8f835 100644 --- a/qcsrc/common/weapons/weapon/hagar.qc +++ b/qcsrc/common/weapons/weapon/hagar.qc @@ -379,16 +379,9 @@ void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int W_Hagar_Attack(thiswep, actor, weaponentity); - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor(actor); - int theframe = WFRAME_FIRE1; - entity this = actor.(weaponentity); - if(this) - { - if(this.wframe == WFRAME_FIRE1) - theframe = WFRAME_DONTCHANGE; - } - weapon_thinkf(actor, weaponentity, theframe, WEP_CVAR_PRI(hagar, refire), W_Hagar_Attack_Auto); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor(actor); + actor.(weaponentity).wframe = WFRAME_FIRE1; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR_PRI(hagar, refire), W_Hagar_Attack_Auto); } METHOD(Hagar, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) diff --git a/qcsrc/common/weapons/weapon/hlac.qc b/qcsrc/common/weapons/weapon/hlac.qc index 9449a24f1..816ddae36 100644 --- a/qcsrc/common/weapons/weapon/hlac.qc +++ b/qcsrc/common/weapons/weapon/hlac.qc @@ -134,8 +134,7 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int return; } - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(actor); W_HLAC_Attack(thiswep, actor, weaponentity); actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index 190dddd39..607f1fcdd 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -56,9 +56,8 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we actor.punchangle_x = random() - 0.5; actor.punchangle_y = random() - 0.5; } - int slot = weaponslot(weaponentity); // this attack_finished just enforces a cooldown at the end of a burst - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); if(actor.(weaponentity).misc_bulletcounter == 1) fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, EFFECT_BULLET); @@ -152,8 +151,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); } - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto); } @@ -182,8 +180,7 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; if(actor.(weaponentity).misc_bulletcounter == 0) { - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready); } else diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index 29192ac58..6063c666e 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -74,8 +74,7 @@ void W_MineLayer_Explode(entity this, entity directhitentity) if(!thiswep.wr_checkammo1(thiswep, own, weaponentity)) { own.cnt = thiswep.m_id; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(own, slot) = time; + ATTACK_FINISHED(own, weaponentity) = time; own.(weaponentity).m_switchweapon = w_getbestweapon(own, weaponentity); } } @@ -106,8 +105,7 @@ void W_MineLayer_DoRemoteExplode(entity this) if(!thiswep.wr_checkammo1(thiswep, own, weaponentity)) { own.cnt = thiswep.m_id; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(own, slot) = time; + ATTACK_FINISHED(own, weaponentity) = time; own.(weaponentity).m_switchweapon = w_getbestweapon(own, weaponentity); } } @@ -464,9 +462,8 @@ METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponent } METHOD(MineLayer, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { - //int slot = 0; // TODO: unhardcode // actually do // don't switch while placing a mine - //if(ATTACK_FINISHED(actor, slot) <= time || PS(actor).m_weapon != WEP_MINE_LAYER) + //if(ATTACK_FINISHED(actor, weaponentity) <= time || PS(actor).m_weapon != WEP_MINE_LAYER) //{ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(minelayer, ammo); ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(minelayer, ammo); diff --git a/qcsrc/common/weapons/weapon/rifle.qc b/qcsrc/common/weapons/weapon/rifle.qc index bf9900b97..d4d692ec7 100644 --- a/qcsrc/common/weapons/weapon/rifle.qc +++ b/qcsrc/common/weapons/weapon/rifle.qc @@ -47,10 +47,9 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent float r, af; Weapon sw = actor.(weaponentity).m_switchweapon; // make it not detect weapon changes as reason to abort firing - int slot = weaponslot(weaponentity); - af = ATTACK_FINISHED(actor, slot); + af = ATTACK_FINISHED(actor, weaponentity); actor.(weaponentity).m_switchweapon = actor.(weaponentity).m_weapon; - ATTACK_FINISHED(actor, slot) = time; + ATTACK_FINISHED(actor, weaponentity) = time; r = weapon_prepareattack(thiswep, actor, weaponentity, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire); if(actor.(weaponentity).m_switchweapon == actor.(weaponentity).m_weapon) actor.(weaponentity).m_switchweapon = sw; @@ -61,7 +60,7 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent } else { - ATTACK_FINISHED(actor, slot) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time + ATTACK_FINISHED(actor, weaponentity) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time } } diff --git a/qcsrc/lib/warpzone/client.qc b/qcsrc/lib/warpzone/client.qc index 3a6765e9d..7f8b0cdc8 100644 --- a/qcsrc/lib/warpzone/client.qc +++ b/qcsrc/lib/warpzone/client.qc @@ -28,6 +28,8 @@ void WarpZone_Fade_PreDraw(entity this) void WarpZone_Touch(entity this, entity toucher); NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) { + if(!warpzone_warpzones_exist) + cvar_settemp("r_water", "1"); // HACK for DarkPlaces: always enable reflections when a map has warpzones warpzone_warpzones_exist = 1; if (!this.enemy) { @@ -84,6 +86,8 @@ NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) NET_HANDLE(ENT_CLIENT_WARPZONE_CAMERA, bool isnew) { + if(!warpzone_cameras_exist) + cvar_settemp("r_water", "1"); // HACK for DarkPlaces: always enable reflections when a map has cameras warpzone_cameras_exist = 1; this.classname = "func_warpzone_camera"; diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc index 2a2144598..ba09c311e 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc @@ -52,23 +52,8 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax, 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; - } + Gametype gt = MapInfo_CurrentGametype(); + gt.m_configuremenu(gt, me, GameType_ConfigureSliders); } entity makeXonoticServerCreateTab() diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index f5ba20c8a..63f1e0179 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -1402,7 +1402,7 @@ void havocbot_chooseweapon(entity this, .entity weaponentity) // Should it do a weapon combo? float af, ct, combo_time, combo; - af = ATTACK_FINISHED(this, 0); + af = ATTACK_FINISHED(this, weaponentity); ct = autocvar_bot_ai_weapon_combo_threshold; // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc index 48975b836..555f6fc58 100644 --- a/qcsrc/server/bot/default/scripting.qc +++ b/qcsrc/server/bot/default/scripting.qc @@ -1084,12 +1084,12 @@ float bot_cmd_debug_assert_canfire(entity this) LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by weaponentity state"); } } - else if(ATTACK_FINISHED(this, slot) > time) + else if(ATTACK_FINISHED(this, weaponentity) > time) { if(f) { this.colormod = '8 0 8'; - LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)"); + LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, weaponentity) - time), " seconds left)"); } } else if(this.(weaponentity).tuba_note) @@ -1105,7 +1105,7 @@ float bot_cmd_debug_assert_canfire(entity this) if(!f) { this.colormod = '8 8 0'; - LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left"); + LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, weaponentity) - time), " seconds left"); } } diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 90bc1fb20..e667906f1 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -202,10 +202,7 @@ float bot_waypoints_for_items; #else #define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).attack_finished_single[slot]) #endif -#define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, ent.(weaponentity).m_weapon.m_id, slot) - -// assault game mode: Which team is attacking in this round? -float assault_attacker_team; +#define ATTACK_FINISHED(ent, w) ATTACK_FINISHED_FOR(ent, ent.(w).m_weapon.m_id, weaponslot(w)) // speedrun: when 1, player auto teleports back when capture timeout happens .float speedrunning; diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc index cce596399..8a17ef6a1 100644 --- a/qcsrc/server/impulse.qc +++ b/qcsrc/server/impulse.qc @@ -149,7 +149,7 @@ X(9, next) for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \ { \ .entity weaponentity = weaponentities[slot]; \ - W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i), weaponentity); \ + W_SwitchWeapon_TryOthers(this, Weapons_from(WEP_FIRST + i), weaponentity); \ if(slot == 0 && autocvar_g_weaponswitch_debug != 1) \ break; \ } \ diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index 8a7bac1b9..0dc71ddcc 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -106,12 +106,12 @@ bool accuracy_isgooddamage(entity attacker, entity targ) if (mutator_check == MUT_ACCADD_INVALID) return true; if (mutator_check != MUT_ACCADD_VALID) return false; - if (!IS_CLIENT(targ)) return false; + if (!IS_CLIENT(targ) || !IS_CLIENT(attacker)) return false; return true; } bool accuracy_canbegooddamage(entity attacker) { - return !warmup_stage; + return !warmup_stage && IS_CLIENT(attacker); } diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc index 4af13e102..5912261db 100644 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@ -266,20 +266,34 @@ void W_SwitchToOtherWeapon(entity this, .entity weaponentity) W_SwitchWeapon_Force(this, ww, weaponentity); } -void W_SwitchWeapon(entity this, Weapon w, .entity weaponentity) +bool W_SwitchWeapon(entity this, Weapon w, .entity weaponentity) { if(this.(weaponentity).m_switchweapon != w) { if(client_hasweapon(this, w, weaponentity, true, true)) + { W_SwitchWeapon_Force(this, w, weaponentity); + return true; + } else + { this.(weaponentity).selectweapon = w.m_id; // update selectweapon anyway + return false; + } } else if(!forbidWeaponUse(this)) { entity actor = this; w.wr_reload(w, actor, weaponentity); } + + return true; // player already has the weapon out or needs to reload +} + +void W_SwitchWeapon_TryOthers(entity this, Weapon w, .entity weaponentity) +{ + if(!W_SwitchWeapon(this, w, weaponentity)) + W_NextWeaponOnImpulse(this, w.impulse, weaponentity); } void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity) diff --git a/qcsrc/server/weapons/selection.qh b/qcsrc/server/weapons/selection.qh index eea33ddb7..dd21e6419 100644 --- a/qcsrc/server/weapons/selection.qh +++ b/qcsrc/server/weapons/selection.qh @@ -18,7 +18,8 @@ void W_SwitchWeapon_Force(Player this, Weapon w, .entity weaponentity); // perform weapon to attack (weaponstate and attack_finished check is here) void W_SwitchToOtherWeapon(entity this, .entity weaponentity); -void W_SwitchWeapon(entity this, Weapon imp, .entity weaponentity); +bool W_SwitchWeapon(entity this, Weapon imp, .entity weaponentity); // returns false if the player does not have the weapon +void W_SwitchWeapon_TryOthers(entity this, Weapon imp, .entity weaponentity); void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity); diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index 86988b6e0..4a45b257a 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -292,9 +292,8 @@ bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponenti if (attacktime >= 0) { - int slot = weaponslot(weaponentity); // don't fire if previous attack is not finished - if (ATTACK_FINISHED(actor, slot) > time + actor.(weaponentity).weapon_frametime * 0.5) return false; + if (ATTACK_FINISHED(actor, weaponentity) > time + actor.(weaponentity).weapon_frametime * 0.5) return false; entity this = actor.(weaponentity); // don't fire while changing weapon if (this.state != WS_READY) return false; @@ -313,16 +312,17 @@ void weapon_prepareattack_do(entity actor, .entity weaponentity, bool secondary, // if the weapon hasn't been firing continuously, reset the timer if (attacktime >= 0) { - int slot = weaponslot(weaponentity); - if (ATTACK_FINISHED(actor, slot) < time - this.weapon_frametime * 1.5) + if (ATTACK_FINISHED(actor, weaponentity) < time - this.weapon_frametime * 1.5) { - ATTACK_FINISHED(actor, slot) = time; + ATTACK_FINISHED(actor, weaponentity) = time; // dprint("resetting attack finished to ", ftos(time), "\n"); } - ATTACK_FINISHED(actor, slot) = ATTACK_FINISHED(actor, slot) + attacktime * W_WeaponRateFactor(actor); + float arate = W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = ATTACK_FINISHED(actor, weaponentity) + attacktime * arate; if(autocvar_g_weaponswitch_debug_alternate && W_DualWielding(actor)) { + int slot = weaponslot(weaponentity); for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) { if(slot == wepslot) @@ -330,15 +330,17 @@ void weapon_prepareattack_do(entity actor, .entity weaponentity, bool secondary, .entity wepent = weaponentities[wepslot]; if(actor.(wepent) && actor.(wepent).m_weapon != WEP_Null) { - if (ATTACK_FINISHED(actor, wepslot) < time - actor.(wepent).weapon_frametime * 1.5) - ATTACK_FINISHED(actor, wepslot) = time; - ATTACK_FINISHED(actor, wepslot) = ATTACK_FINISHED(actor, wepslot) + (attacktime * W_WeaponRateFactor(actor)) * 0.5; + if(ATTACK_FINISHED(actor, wepent) > time + actor.(wepent).weapon_frametime * 0.5) + continue; // still cooling down! + if (ATTACK_FINISHED(actor, wepent) < time - actor.(wepent).weapon_frametime * 1.5) + ATTACK_FINISHED(actor, wepent) = time; + ATTACK_FINISHED(actor, wepent) = ATTACK_FINISHED(actor, wepent) + (attacktime * arate) / MAX_WEAPONSLOTS; } } } } this.bulletcounter += 1; - // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, slot)), "\n"); + // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, weaponentity)), "\n"); } bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime) @@ -554,7 +556,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) entity oldwep = this.m_weapon; // set up weapon switch think in the future, and start drop anim - if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponslot(weaponentity)) <= time + this.weapon_frametime * 0.5) + if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponentity) <= time + this.weapon_frametime * 0.5) { sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM); this.state = WS_DROP; @@ -567,7 +569,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) // LordHavoc: network timing test code // if (actor.button0) - // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, slot)), " >= ", ftos(this.weapon_nextthink), "\n"); + // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, weaponentity)), " >= ", ftos(this.weapon_nextthink), "\n"); Weapon w = this.m_weapon; @@ -697,7 +699,7 @@ void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use, .entity weaponenti { backtrace(sprintf( "W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... " - "Please notify Samual immediately with a copy of this backtrace!\n", + "Please notify the developers immediately with a copy of this backtrace!\n", ammo_use, wep.netname, GetAmmoPicture(wep.ammo_type), @@ -743,7 +745,7 @@ void W_ReloadedAndReady(Weapon thiswep, entity actor, .entity weaponentity, int // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, // so your weapon is disabled for a few seconds without reason - // ATTACK_FINISHED(actor, slot) -= w_ent.reload_time - 1; + // ATTACK_FINISHED(actor, weaponentity) -= w_ent.reload_time - 1; w_ready(wpn, actor, weaponentity, PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1)); } @@ -816,7 +818,7 @@ void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sen // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, // so your weapon is disabled for a few seconds without reason - // ATTACK_FINISHED(actor, slot) = max(time, ATTACK_FINISHED(actor, slot)) + this.reload_time + 1; + // ATTACK_FINISHED(actor, weaponentity) = max(time, ATTACK_FINISHED(actor, weaponentity)) + this.reload_time + 1; weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, this.reload_time, W_ReloadedAndReady);