From: Mario Date: Tue, 20 Jan 2015 02:40:00 +0000 (+1100) Subject: Merge branch 'master' into Mario/ons_updates X-Git-Tag: xonotic-v0.8.2~2050^2~9 X-Git-Url: http://de.git.xonotic.org/?a=commitdiff_plain;h=e80962600618cee1bb882cf785a2f3011466e12c;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into Mario/ons_updates Conflicts: qcsrc/client/View.qc qcsrc/client/scoreboard.qc qcsrc/common/constants.qh qcsrc/common/mapinfo.qh qcsrc/common/notifications.qh qcsrc/server/autocvars.qh qcsrc/server/bot/havocbot/roles.qc qcsrc/server/g_damage.qc qcsrc/server/mutators/gamemode_onslaught.qc qcsrc/server/progs.src --- e80962600618cee1bb882cf785a2f3011466e12c diff --cc defaultXonotic.cfg index cf6111890,df7044ce5..7444b2e15 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@@ -220,13 -221,15 +221,16 @@@ set sv_ready_restart 0 "if set to 1 all set sv_ready_restart_after_countdown 0 "if set to 1 the players and map items are reset after the countdown ended, otherwise they're reset already at the beginning of the countdown" set sv_ready_restart_repeatable 0 "allows the players to restart the game as often as needed" - seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy" + seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy, 1: same pitch 2: increase pitch with more damage 3: decrease pitch with more damage" set cl_hitsound_antispam_time 0.05 "don't play the hitsound more often than this" + seta cl_hitsound_min_pitch 0.75 "minimum pitch of hit sound" + seta cl_hitsound_max_pitch 1.5 "maximum pitch of hit sound" + seta cl_hitsound_nom_damage 25 "damage amount at which hitsound bases pitch off" - seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead" + seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead; set to 2 to active the effect only when the corpse doesn't move anymore" seta cl_eventchase_nexball 1 "camera goes into 3rd person mode when in nexball game-mode" seta cl_eventchase_distance 140 "final camera distance" +seta cl_eventchase_distance 400 "final camera distance while viewing generator explosion" seta cl_eventchase_speed 1.3 "how fast the camera slides back, 0 is instant" seta cl_eventchase_maxs "12 12 8" "max size of eventchase camera bbox" seta cl_eventchase_mins "-12 -12 -8" "min size of eventchase camera bbox" diff --cc qcsrc/client/View.qc index 375ed9036,8a725774e..734ef3233 --- a/qcsrc/client/View.qc +++ b/qcsrc/client/View.qc @@@ -493,38 -1050,18 +1051,39 @@@ void CSQC_UpdateView(float w, float h // event chase camera if(autocvar_chase_active <= 0) // greater than 0 means it's enabled manually, and this code is skipped { - WepSet weapons_stat = WepSet_GetFromStat(); - if(WantEventchase()) + float ons_roundlost = (gametype == MAPINFO_TYPE_ONSLAUGHT && getstati(STAT_ROUNDLOST)); + entity gen = world; + + if(ons_roundlost) + { + entity e; + for(e = world; (e = find(e, classname, "onslaught_generator")); ) + { + if(e.health <= 0) + { + gen = e; + break; + } + } + if(!gen) + ons_roundlost = FALSE; // don't enforce the 3rd person camera if there is no dead generator to show + } - if(((spectatee_status >= 0 && (autocvar_cl_eventchase_death && is_dead)) || intermission || ons_roundlost) && !autocvar_cl_orthoview || (autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(weapons_stat & WepSet_FromWeapon(WEP_PORTO)))) ++ if(WantEventchase() || (!autocvar_cl_orthoview && ons_roundlost)) { + eventchase_running = TRUE; + // make special vector since we can't use view_origin (It is one frame old as of this code, it gets set later with the results this code makes.) vector current_view_origin = (csqcplayer ? csqcplayer.origin : pmove_org); + if(ons_roundlost) { current_view_origin = gen.origin; } // detect maximum viewoffset and use it - if(autocvar_cl_eventchase_viewoffset) + vector view_offset = autocvar_cl_eventchase_viewoffset; + if(ons_roundlost) { view_offset = autocvar_cl_eventchase_generator_viewoffset; } + + if(view_offset) { - WarpZone_TraceLine(current_view_origin, current_view_origin + autocvar_cl_eventchase_viewoffset + ('0 0 1' * autocvar_cl_eventchase_maxs_z), MOVE_WORLDONLY, self); - if(trace_fraction == 1) { current_view_origin += autocvar_cl_eventchase_viewoffset; } + WarpZone_TraceLine(current_view_origin, current_view_origin + view_offset + ('0 0 1' * autocvar_cl_eventchase_maxs_z), MOVE_WORLDONLY, self); + if(trace_fraction == 1) { current_view_origin += view_offset; } else { current_view_origin_z += max(0, (trace_endpos_z - current_view_origin_z) - autocvar_cl_eventchase_maxs_z); } } diff --cc qcsrc/client/scoreboard.qc index 7ab8576ba,f5b3206ba..c8609dc32 --- a/qcsrc/client/scoreboard.qc +++ b/qcsrc/client/scoreboard.qc @@@ -287,19 -287,15 +287,15 @@@ void Cmd_HUD_Help( "other gamemodes except DM.\n")); } - string HUD_DefaultColumnLayout() - { - return strcat( // fteqcc sucks - "ping pl name | ", - "-teams,race,lms/kills +freezetag/kills -teams,lms/deaths +freezetag/deaths -teams,lms,race,ka/suicides +freezetag/suicides -race,dm,tdm,ka,freezetag/frags ", // tdm already has this in "score" - "+tdm/kills +tdm/deaths +tdm/suicides ", - "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes ", - "+lms/lives +lms/rank ", - "+kh/caps +kh/pushes +kh/destroyed ", - "?+race/laps ?+race/time ?+race/fastest ", - "+as/objectives +nexball/faults +nexball/goals +ka/pickups +ka/bckills +ka/bctime +freezetag/revivals ", - "-lms,race,nexball/score"); - } + #define HUD_DefaultColumnLayout() \ + "ping pl name | " \ + "-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \ -"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \ ++"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \ + "+lms/lives +lms/rank " \ + "+kh/caps +kh/pushes +kh/destroyed " \ + "?+race/laps ?+race/time ?+race/fastest " \ + "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \ + "-lms,race,nb/score" void Cmd_HUD_SetFields(float argc) { diff --cc qcsrc/common/constants.qh index 73662b617,8586cffa9..e93f4b400 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@@ -100,9 -100,9 +100,11 @@@ const float ENT_CLIENT_ELIMINATEDPLAYER const float ENT_CLIENT_TURRET = 40; const float ENT_CLIENT_AUXILIARYXHAIR = 50; const float ENT_CLIENT_VEHICLE = 60; +const float ENT_CLIENT_GENERATOR = 70; +const float ENT_CLIENT_CONTROLPOINT_ICON = 71; + const float ENT_CLIENT_HEALING_ORB = 80; + const float SPRITERULE_DEFAULT = 0; const float SPRITERULE_TEAMPLAY = 1; diff --cc qcsrc/common/mapinfo.qh index 656b80d5f,bbcc267b2..16d915883 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@@ -33,52 -37,52 +37,52 @@@ entity MapInfo_Type_last #define IS_GAMETYPE(NAME) \ (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME) - REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0"); + REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,FALSE,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies")); #define g_dm IS_GAMETYPE(DEATHMATCH) - REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0"); + REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,FALSE,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); #define g_lms IS_GAMETYPE(LMS) - REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0"); + REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,FALSE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); #define g_race IS_GAMETYPE(RACE) - REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1"); + REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,FALSE,"timelimit=20 skill=-1",_("Race for fastest time")); #define g_cts IS_GAMETYPE(CTS) - REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0"); + REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,TRUE,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates")); #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH) - REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0"); + REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,TRUE,"timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it")); #define g_ctf IS_GAMETYPE(CTF) - REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0"); + REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,TRUE,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); #define g_ca IS_GAMETYPE(CA) - REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0"); + REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,TRUE,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win")); #define g_domination IS_GAMETYPE(DOMINATION) - REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0"); + REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,TRUE,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")); #define g_keyhunt IS_GAMETYPE(KEYHUNT) - REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20"); + REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,TRUE,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); #define g_assault IS_GAMETYPE(ASSAULT) - REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"pointlimit=1 timelimit=30"); -REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,TRUE,"timelimit=20",_("Capture control points to reach and destroy the enemy generator")); ++REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,TRUE,"pointlimit = 1 timelimit=30",_("Capture control points to reach and destroy the enemy generator")); #define g_onslaught IS_GAMETYPE(ONSLAUGHT) - REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0"); + REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,TRUE,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports")); #define g_nexball IS_GAMETYPE(NEXBALL) - REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0"); + REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,TRUE,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them")); #define g_freezetag IS_GAMETYPE(FREEZETAG) - REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30"); + REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,TRUE,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); #define g_keepaway IS_GAMETYPE(KEEPAWAY) - REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0"); + REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,FALSE,"pointlimit=50 teams=0",_("Survive against waves of monsters")); #define g_invasion IS_GAMETYPE(INVASION) - const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for minstagib-only maps + const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps const float MAPINFO_FEATURE_VEHICLES = 2; const float MAPINFO_FEATURE_TURRETS = 4; const float MAPINFO_FEATURE_MONSTERS = 8; diff --cc qcsrc/common/notifications.qh index 3d212d635,0bfd2e5f3..7753288c0 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@@ -623,10 -660,9 +664,10 @@@ void Send_Notification_WOCOVA MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You froze ^BG%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN, 1, 0, "s1", NO_CPID, "0 0", _("^K1You were frozen by ^BG%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You revived ^BG%s"), "") \ - MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_FALL, 0, 0, "", NO_CPID, "0 0", _("^K3You revived yourself"), "") \ + MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_SELF, 0, 0, "", NO_CPID, "0 0", _("^K3You revived yourself"), "") \ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED, 1, 0, "s1", NO_CPID, "0 0", _("^K3You were revived by ^BG%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED, 0, 1, "f1", NO_CPID, "0 0", _("^K3You were automatically revived after %s second(s)"), "") \ + MSG_CENTER_NOTIF(1, CENTER_GENERATOR_UNDERATTACK, 0, 0, "", NO_CPID, "0 0", _("^BGThe generator is under attack!"), "") \ MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4, 0, 0, "", CPID_ROUND, "0 0", _("^TC^TT^BG team wins the round"), "") \ MSG_CENTER_NOTIF(1, CENTER_ROUND_PLAYER_WIN, 1, 0, "s1", CPID_ROUND, "0 0", _("^BG%s^BG wins the round"), "") \ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF, 0, 0, "", NO_CPID, "0 0", _("^K1You froze yourself"), "") \ diff --cc qcsrc/common/stats.qh index 000000000,f05702997..3f35a3a66 mode 000000,100644..100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@@ -1,0 -1,290 +1,290 @@@ + // Full list of all stat constants, icnluded in a single location for easy reference + // 255 is the current limit (MAX_CL_STATS - 1), engine will need to be modified if you wish to add more stats + + const float MAX_CL_STATS = 256; + const float STAT_HEALTH = 0; + // 1 empty? + const float STAT_WEAPON = 2; + const float STAT_AMMO = 3; + const float STAT_ARMOR = 4; + const float STAT_WEAPONFRAME = 5; + const float STAT_SHELLS = 6; + const float STAT_NAILS = 7; + const float STAT_ROCKETS = 8; + const float STAT_CELLS = 9; + const float STAT_ACTIVEWEAPON = 10; + const float STAT_TOTALSECRETS = 11; + const float STAT_TOTALMONSTERS = 12; + const float STAT_SECRETS = 13; + const float STAT_MONSTERS = 14; + const float STAT_ITEMS = 15; + const float STAT_VIEWHEIGHT = 16; + // 17 empty? + // 18 empty? + // 19 empty? + // 20 empty? + const float STAT_VIEWZOOM = 21; + // 22 empty? + // 23 empty? + // 24 empty? + // 25 empty? + // 26 empty? + // 27 empty? + // 28 empty? + // 29 empty? + // 30 empty? + // 31 empty? + const float STAT_KH_KEYS = 32; + const float STAT_CTF_STATE = 33; + // 34 empty? + const float STAT_WEAPONS = 35; + const float STAT_SWITCHWEAPON = 36; + const float STAT_GAMESTARTTIME = 37; + const float STAT_STRENGTH_FINISHED = 38; + const float STAT_INVINCIBLE_FINISHED = 39; + // 40 empty? + const float STAT_ARC_HEAT = 41; + const float STAT_PRESSED_KEYS = 42; + const float STAT_ALLOW_OLDVORTEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config + const float STAT_FUEL = 44; + const float STAT_NB_METERSTART = 45; + const float STAT_SHOTORG = 46; // compressShotOrigin + const float STAT_LEADLIMIT = 47; + const float STAT_WEAPON_CLIPLOAD = 48; + const float STAT_WEAPON_CLIPSIZE = 49; + const float STAT_VORTEX_CHARGE = 50; + const float STAT_LAST_PICKUP = 51; + const float STAT_HUD = 52; + const float STAT_VORTEX_CHARGEPOOL = 53; + const float STAT_HIT_TIME = 54; + const float STAT_DAMAGE_DEALT_TOTAL = 55; + const float STAT_TYPEHIT_TIME = 56; + const float STAT_LAYED_MINES = 57; + const float STAT_HAGAR_LOAD = 58; + const float STAT_SWITCHINGWEAPON = 59; + const float STAT_SUPERWEAPONS_FINISHED = 60; + const float STAT_VEHICLESTAT_HEALTH = 61; + const float STAT_VEHICLESTAT_SHIELD = 62; + const float STAT_VEHICLESTAT_ENERGY = 63; + const float STAT_VEHICLESTAT_AMMO1 = 64; + const float STAT_VEHICLESTAT_RELOAD1 = 65; + const float STAT_VEHICLESTAT_AMMO2 = 66; + const float STAT_VEHICLESTAT_RELOAD2 = 67; + const float STAT_VEHICLESTAT_W2MODE = 68; + const float STAT_NADE_TIMER = 69; + const float STAT_SECRETS_TOTAL = 70; + const float STAT_SECRETS_FOUND = 71; + const float STAT_RESPAWN_TIME = 72; + const float STAT_ROUNDSTARTTIME = 73; + const float STAT_WEAPONS2 = 74; + const float STAT_WEAPONS3 = 75; + const float STAT_MONSTERS_TOTAL = 76; + const float STAT_MONSTERS_KILLED = 77; + const float STAT_BUFFS = 78; + const float STAT_NADE_BONUS = 79; + const float STAT_NADE_BONUS_TYPE = 80; + const float STAT_NADE_BONUS_SCORE = 81; + const float STAT_HEALING_ORB = 82; + const float STAT_HEALING_ORB_ALPHA = 83; + const float STAT_PLASMA = 84; + const float STAT_OK_AMMO_CHARGE = 85; + const float STAT_OK_AMMO_CHARGEPOOl = 86; -// 87 empty? ++const float STAT_ROUNDLOST = 87; + // 88 empty? + // 89 empty? + // 90 empty? + // 91 empty? + // 92 empty? + // 93 empty? + // 94 empty? + // 95 empty? + // 96 empty? + // 97 empty? + // 98 empty? + // 99 empty? + + + /* The following stats change depending on the gamemode, so can share the same ID */ + // IDs 100 to 104 reserved for gamemodes + + // freeze tag, clan arena, jailbreak + const float STAT_REDALIVE = 100; + const float STAT_BLUEALIVE = 101; + const float STAT_YELLOWALIVE = 102; + const float STAT_PINKALIVE = 103; + + // domination + const float STAT_DOM_TOTAL_PPS = 100; + const float STAT_DOM_PPS_RED = 101; + const float STAT_DOM_PPS_BLUE = 102; + const float STAT_DOM_PPS_YELLOW = 103; + const float STAT_DOM_PPS_PINK = 104; + + // vip + const float STAT_VIP = 100; + const float STAT_VIP_RED = 101; + const float STAT_VIP_BLUE = 102; + const float STAT_VIP_YELLOW = 103; + const float STAT_VIP_PINK = 104; + + // key hunt + const float STAT_KH_REDKEY_TEAM = 100; + const float STAT_KH_BLUEKEY_TEAM = 101; + const float STAT_KH_YELLOWKEY_TEAM = 102; + const float STAT_KH_PINKKEY_TEAM = 103; + + /* Gamemode-specific stats end here */ + + + const float STAT_FROZEN = 105; + const float STAT_REVIVE_PROGRESS = 106; + // 107 empty? + // 108 empty? + // 109 empty? + // 110 empty? + // 111 empty? + // 112 empty? + // 113 empty? + // 114 empty? + // 115 empty? + // 116 empty? + // 117 empty? + // 118 empty? + // 119 empty? + // 120 empty? + // 121 empty? + // 122 empty? + // 123 empty? + // 124 empty? + // 125 empty? + // 126 empty? + // 127 empty? + // 128 empty? + // 129 empty? + // 130 empty? + // 131 empty? + // 132 empty? + // 133 empty? + // 134 empty? + // 135 empty? + // 136 empty? + // 137 empty? + // 138 empty? + // 139 empty? + // 140 empty? + // 141 empty? + // 142 empty? + // 143 empty? + // 144 empty? + // 145 empty? + // 146 empty? + // 147 empty? + // 148 empty? + // 149 empty? + // 150 empty? + // 151 empty? + // 152 empty? + // 153 empty? + // 154 empty? + // 155 empty? + // 156 empty? + // 157 empty? + // 158 empty? + // 159 empty? + // 160 empty? + // 161 empty? + // 162 empty? + // 162 empty? + // 163 empty? + // 164 empty? + // 165 empty? + // 166 empty? + // 167 empty? + // 168 empty? + // 169 empty? + // 170 empty? + // 171 empty? + // 172 empty? + // 173 empty? + // 174 empty? + // 175 empty? + // 176 empty? + // 177 empty? + // 178 empty? + // 179 empty? + // 180 empty? + // 181 empty? + // 182 empty? + // 183 empty? + // 184 empty? + // 185 empty? + // 186 empty? + // 187 empty? + // 188 empty? + // 189 empty? + // 190 empty? + // 191 empty? + // 192 empty? + // 193 empty? + // 194 empty? + // 195 empty? + // 196 empty? + // 197 empty? + // 198 empty? + // 199 empty? + // 200 empty? + // 201 empty? + // 202 empty? + // 203 empty? + // 204 empty? + // 205 empty? + // 206 empty? + // 207 empty? + // 208 empty? + // 209 empty? + // 210 empty? + // 211 empty? + // 212 empty? + // 213 empty? + // 214 empty? + // 215 empty? + // 216 empty? + // 217 empty? + // 218 empty? + // 219 empty? + const float STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220; + const float STAT_MOVEVARS_AIRCONTROL_PENALTY = 221; + const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222; + const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223; + const float STAT_MOVEVARS_AIRCONTROL_POWER = 224; + const float STAT_MOVEFLAGS = 225; + const float STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL = 226; + const float STAT_MOVEVARS_WARSOWBUNNY_ACCEL = 227; + const float STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED = 228; + const float STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL = 229; + const float STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO = 230; + const float STAT_MOVEVARS_AIRSTOPACCELERATE = 231; + const float STAT_MOVEVARS_AIRSTRAFEACCELERATE = 232; + const float STAT_MOVEVARS_MAXAIRSTRAFESPEED = 233; + const float STAT_MOVEVARS_AIRCONTROL = 234; + const float STAT_FRAGLIMIT = 235; + const float STAT_TIMELIMIT = 236; + const float STAT_MOVEVARS_WALLFRICTION = 237; + const float STAT_MOVEVARS_FRICTION = 238; + const float STAT_MOVEVARS_WATERFRICTION = 239; + const float STAT_MOVEVARS_TICRATE = 240; + const float STAT_MOVEVARS_TIMESCALE = 241; + const float STAT_MOVEVARS_GRAVITY = 242; + const float STAT_MOVEVARS_STOPSPEED = 243; + const float STAT_MOVEVARS_MAXSPEED = 244; + const float STAT_MOVEVARS_SPECTATORMAXSPEED = 245; + const float STAT_MOVEVARS_ACCELERATE = 246; + const float STAT_MOVEVARS_AIRACCELERATE = 247; + const float STAT_MOVEVARS_WATERACCELERATE = 248; + const float STAT_MOVEVARS_ENTGRAVITY = 249; + const float STAT_MOVEVARS_JUMPVELOCITY = 250; + const float STAT_MOVEVARS_EDGEFRICTION = 251; + const float STAT_MOVEVARS_MAXAIRSPEED = 252; + const float STAT_MOVEVARS_STEPHEIGHT = 253; + const float STAT_MOVEVARS_AIRACCEL_QW = 254; + const float STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION = 255; diff --cc qcsrc/server/autocvars.qh index 5d39ccf9a,168045bd0..460d8354b --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@@ -918,7 -431,13 +431,8 @@@ float autocvar_g_nix_with_powerups float autocvar_g_nodepthtestitems; float autocvar_g_nodepthtestplayers; float autocvar_g_norecoil; -float autocvar_g_onslaught_cp_buildhealth; -float autocvar_g_onslaught_cp_buildtime; -float autocvar_g_onslaught_cp_health; -float autocvar_g_onslaught_cp_regen; -float autocvar_g_onslaught_gen_health; float autocvar_g_pickup_cells_max; + float autocvar_g_pickup_plasma_max; float autocvar_g_pickup_fuel_max; float autocvar_g_pickup_items; float autocvar_g_pickup_nails_max; @@@ -1278,27 -850,33 +845,56 @@@ float autocvar_g_spawn_near_teammate_ig float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; float autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; float autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; +float autocvar_g_onslaught_debug; +float autocvar_g_onslaught_teleport_wait; +float autocvar_g_onslaught_spawn_at_controlpoints; +var float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; +float autocvar_g_onslaught_spawn_at_controlpoints_random; +float autocvar_g_onslaught_spawn_at_generator; +float autocvar_g_onslaught_spawn_at_generator_chance; +float autocvar_g_onslaught_spawn_at_generator_random; +float autocvar_g_onslaught_cp_proxydecap; +var float autocvar_g_onslaught_cp_proxydecap_distance = 512; +var float autocvar_g_onslaught_cp_proxydecap_dps = 100; +float autocvar_g_onslaught_cp_buildhealth; +float autocvar_g_onslaught_cp_buildtime; +float autocvar_g_onslaught_cp_health; +float autocvar_g_onslaught_cp_regen; +float autocvar_g_onslaught_gen_health; +var float autocvar_g_onslaught_shield_force = 100; +float autocvar_g_onslaught_allow_vehicle_touch; +float autocvar_g_onslaught_round_timelimit; +float autocvar_g_onslaught_point_limit; +float autocvar_g_onslaught_warmup; +float autocvar_g_onslaught_teleport_radius; +float autocvar_g_onslaught_spawn_choose; +float autocvar_g_onslaught_click_radius; + float autocvar_g_buffs_waypoint_distance; + float autocvar_g_buffs_randomize; + float autocvar_g_buffs_random_lifetime; + float autocvar_g_buffs_random_location; + float autocvar_g_buffs_random_location_attempts; + float autocvar_g_buffs_spawn_count; + float autocvar_g_buffs_replace_powerups; + float autocvar_g_buffs_cooldown_activate; + float autocvar_g_buffs_cooldown_respawn; + float autocvar_g_buffs_resistance_blockpercent; + float autocvar_g_buffs_medic_survive_chance; + float autocvar_g_buffs_medic_survive_health; + float autocvar_g_buffs_medic_rot; + float autocvar_g_buffs_medic_max; + float autocvar_g_buffs_medic_regen; + float autocvar_g_buffs_vengeance_damage_multiplier; + float autocvar_g_buffs_bash_force; + float autocvar_g_buffs_bash_force_self; + float autocvar_g_buffs_disability_time; + float autocvar_g_buffs_disability_speed; + float autocvar_g_buffs_disability_rate; + float autocvar_g_buffs_speed_speed; + float autocvar_g_buffs_speed_rate; + float autocvar_g_buffs_speed_damage_take; + float autocvar_g_buffs_speed_regen; + float autocvar_g_buffs_vampire_damage_steal; + float autocvar_g_buffs_invisible_alpha; + float autocvar_g_buffs_flight_gravity; + float autocvar_g_buffs_jump_height; - diff --cc qcsrc/server/bot/havocbot/roles.qc index 58aa893c3,7e3ddbb43..c6f0efa6d --- a/qcsrc/server/bot/havocbot/roles.qc +++ b/qcsrc/server/bot/havocbot/roles.qc @@@ -273,8 -237,8 +237,6 @@@ void havocbot_chooserole( return; else if (g_keyhunt) havocbot_chooserole_kh(); - else if (g_race || g_cts) - havocbot_chooserole_race(); - else if (g_onslaught) - havocbot_chooserole_ons(); else // assume anything else is deathmatch havocbot_chooserole_dm(); } diff --cc qcsrc/server/g_damage.qc index c6ba6a12c,4840e15dd..8887f07ef --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@@ -732,9 -864,9 +864,9 @@@ void Damage (entity targ, entity inflic else victim = targ; - if(IS_PLAYER(victim) || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET || victim.flags & FL_MONSTER || victim.classname == "func_assault_destructible" || (victim.classname == "onslaught_generator" && !victim.isshielded) || (victim.classname == "onslaught_controlpoint_icon" && !victim.owner.isshielded)) - if(IS_PLAYER(victim) || (victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (victim.flags & FL_MONSTER)) ++ if(IS_PLAYER(victim) || (victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (victim.flags & FL_MONSTER) || victim.classname == "func_assault_destructible" || (victim.classname == "onslaught_generator" && !victim.isshielded) || (victim.classname == "onslaught_controlpoint_icon" && !victim.owner.isshielded)) { - if(DIFF_TEAM(victim, attacker)) + if(DIFF_TEAM(victim, attacker) && !victim.frozen) { if(damage > 0) { diff --cc qcsrc/server/mutators/gamemode_onslaught.qc index ecf219400,74cba2897..e47999e74 --- a/qcsrc/server/mutators/gamemode_onslaught.qc +++ b/qcsrc/server/mutators/gamemode_onslaught.qc @@@ -358,272 -379,415 +358,272 @@@ float ons_ControlPoint_Attackable(entit return 0; } -float overtime_msg_time; -void onslaught_generator_think() +void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { - float d; - entity e; - self.nextthink = ceil(time + 1); - if (!gameover) + entity oself; + + if(damage <= 0) { return; } + + if (self.owner.isshielded) { - if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) - { - if (!overtime_msg_time) - { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); - overtime_msg_time = time; - } - // self.max_health / 300 gives 5 minutes of overtime. - // control points reduce the overtime duration. - sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NORM); - d = 1; - e = findchain(classname, "onslaught_controlpoint"); - while (e) + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (IS_PLAYER(attacker)) { - if (e.team != self.team) - if (e.islinked) - d = d + 1; - e = e.chain; + play2(attacker, "onslaught/damageblockedbyshield.wav"); + self.pain_finished = time + 1; + attacker.typehitsound += 1; // play both sounds (shield is way too quiet) } - if(autocvar_g_campaign && autocvar__campaign_testrun) - d = d * self.max_health; - else - d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath); - - Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0'); - } - else if (overtime_msg_time) - overtime_msg_time = 0; - - if(!self.isshielded && self.wait < time) - { - self.wait = time + 5; - FOR_EACH_REALPLAYER(e) - { - if(SAME_TEAM(e, self)) - { - Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED); - soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE); // FIXME: Uniqe sound? - } - } - } + return; } -} - -void onslaught_generator_ring_spawn(vector org) -{ - modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25); -} -void onslaught_generator_ray_think() -{ - self.nextthink = time + 0.05; - if(self.count > 10) + if(IS_PLAYER(attacker)) + if(time - ons_notification_time[self.team] > 10) { - self.think = SUB_Remove; - return; + play2team(self.team, "onslaught/controlpoint_underattack.wav"); + ons_notification_time[self.team] = time; } - if(self.count > 5) - self.alpha -= 0.1; + self.health = self.health - damage; + if(self.owner.iscaptured) + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + else - WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / CP_THINKRATE)); ++ WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE)); + self.pain_finished = time + 1; + // particles on every hit + pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1); + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM); else - self.alpha += 0.1; + sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM); - self.scale += 0.2; - self.count +=1; -} + if (self.health < 0) + { + sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname); + + PlayerScore_Add(attacker, SP_ONS_TAKES, 1); + PlayerScore_Add(attacker, SP_SCORE, 10); + + self.owner.goalentity = world; + self.owner.islinked = FALSE; + self.owner.iscaptured = FALSE; + self.owner.team = 0; + self.owner.colormap = 1024; -void onslaught_generator_ray_spawn(vector org) -{ - entity e; - e = spawn(); - setmodel(e, "models/onslaught/ons_ray.md3"); - setorigin(e, org); - e.angles = randomvec() * 360; - e.alpha = 0; - e.scale = random() * 5 + 8; - e.think = onslaught_generator_ray_think; - e.nextthink = time + 0.05; -} + WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); -void onslaught_generator_shockwave_spawn(vector org) -{ - shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5); -} + onslaught_updatelinks(); -void onslaught_generator_damage_think() -{ - if(self.owner.health < 0) - { - self.think = SUB_Remove; - return; - } - self.nextthink = time+0.1; + // Use targets now (somebody make sure this is in the right place..) + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; - // damaged fx (less probable the more damaged is the generator) - if(random() < 0.9 - self.owner.health / self.owner.max_health) - if(random() < 0.01) - { - pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); - sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM); - } - else - pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); -} + self.owner.waslinked = self.owner.islinked; + if(self.owner.model != "models/onslaught/controlpoint_pad.md3") + setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad.md3"); + //setsize(self, '-32 -32 0', '32 32 8'); -void onslaught_generator_damage_spawn(entity gd_owner) -{ - entity e; - e = spawn(); - e.owner = gd_owner; - e.health = self.owner.health; - setorigin(e, gd_owner.origin); - e.think = onslaught_generator_damage_think; - e.nextthink = time+1; + remove(self); + } + + self.SendFlags |= CPSF_STATUS; } -void onslaught_generator_deaththink() +void ons_ControlPoint_Icon_Think() { - vector org; - float i; - - if (!self.count) - self.count = 40; + entity oself; - self.nextthink = time + CP_THINKRATE; ++ self.nextthink = time + ONS_CP_THINKRATE; - // White shockwave - if(self.count==40||self.count==20) + if(autocvar_g_onslaught_cp_proxydecap) { - onslaught_generator_ring_spawn(self.origin); - sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTEN_NORM); - } + float _enemy_count = 0; + float _friendly_count = 0; + float _dist; + entity _player; - // Throw some gibs - if(random() < 0.3) - { - i = random(); - if(i < 0.3) - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE); - else if(i > 0.7) - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE); - else - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE); - } + FOR_EACH_PLAYER(_player) + { + if(!_player.deadflag) + { + _dist = vlen(_player.origin - self.origin); + if(_dist < autocvar_g_onslaught_cp_proxydecap_distance) + { + if(SAME_TEAM(_player, self)) + ++_friendly_count; + else + ++_enemy_count; + } + } + } + - _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * CP_THINKRATE); - _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * CP_THINKRATE); ++ _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); ++ _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - // Spawn fire balls - for(i=0;i < 10;++i) + self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); + self.SendFlags |= CPSF_STATUS; + if(self.health <= 0) + { + ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0'); + return; + } + } + + if (time > self.pain_finished + 5) { - org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); - pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1); + if(self.health < self.max_health) + { + self.health = self.health + self.count; + if (self.health >= self.max_health) + self.health = self.max_health; + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + } } - // Short explosion sound + small explosion - if(random() < 0.25) + if(self.owner.islinked != self.owner.waslinked) { - te_explosion(self.origin); - sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); - } + // unteam the spawnpoint if needed + float t; + t = self.owner.team; + if(!self.owner.islinked) + self.owner.team = 0; + + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; - // Particles - org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); - pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1); + self.owner.team = t; - // rays - if(random() > 0.25 ) - { - onslaught_generator_ray_spawn(self.origin); + self.owner.waslinked = self.owner.islinked; } - // Final explosion - if(self.count==1) + // damaged fx + if(random() < 0.6 - self.health / self.max_health) { - org = self.origin; - te_explosion(org); - onslaught_generator_shockwave_spawn(org); - pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1); - sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - } - else - self.nextthink = time + 0.05; - - self.count = self.count - 1; -} + pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); -void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - float i; - if (damage <= 0) - return; - if(warmup_stage) - return; - if (attacker != self) - { - if (self.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, "onslaught/damageblockedbyshield.wav"); - self.pain_finished = time + 1; - } - return; - } - if (time > self.pain_finished) - { - self.pain_finished = time + 10; - bprint(Team_ColoredFullName(self.team), " generator under attack!\n"); - play2team(self.team, "onslaught/generator_underattack.wav"); - } - } - self.health = self.health - damage; - WaypointSprite_UpdateHealth(self.sprite, self.health); - // choose an animation frame based on health - self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); - // see if the generator is still functional, or dying - if (self.health > 0) - { -#ifdef ONSLAUGHT_SPAM - float h, lh; - lh = ceil(self.lasthealth / 100) * 100; - h = ceil(self.health / 100) * 100; - if(lh != h) - bprint(Team_ColoredFullName(self.team), " generator has less than ", ftos(h), " health remaining\n"); -#endif - self.lasthealth = self.health; + if(random() > 0.8) + sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM); + else if (random() > 0.5) + sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM); } - else if (!warmup_stage) - { - if (attacker == self) - bprint(Team_ColoredFullName(self.team), " generator spontaneously exploded due to overtime!\n"); - else - { - string t; - t = Team_ColoredFullName(attacker.team); - bprint(Team_ColoredFullName(self.team), " generator destroyed by ", t, "!\n"); - } - self.iscaptured = FALSE; - self.islinked = FALSE; - self.isshielded = FALSE; - self.takedamage = DAMAGE_NO; // can't be hurt anymore - self.event_damage = func_null; // won't do anything if hurt - self.count = 0; // reset counter - self.think = onslaught_generator_deaththink; // explosion sequence - self.nextthink = time; // start exploding immediately - self.think(); // do the first explosion now +} - WaypointSprite_UpdateMaxHealth(self.sprite, 0); +void ons_ControlPoint_Icon_BuildThink() +{ + entity oself; + float a; - self.nextthink = time + CP_THINKRATE; - onslaught_updatelinks(); - } ++ self.nextthink = time + ONS_CP_THINKRATE; - if(self.health <= 0) - setmodel(self, "models/onslaught/generator_dead.md3"); - else if(self.health < self.max_health * 0.10) - setmodel(self, "models/onslaught/generator_dmg9.md3"); - else if(self.health < self.max_health * 0.20) - setmodel(self, "models/onslaught/generator_dmg8.md3"); - else if(self.health < self.max_health * 0.30) - setmodel(self, "models/onslaught/generator_dmg7.md3"); - else if(self.health < self.max_health * 0.40) - setmodel(self, "models/onslaught/generator_dmg6.md3"); - else if(self.health < self.max_health * 0.50) - setmodel(self, "models/onslaught/generator_dmg5.md3"); - else if(self.health < self.max_health * 0.60) - setmodel(self, "models/onslaught/generator_dmg4.md3"); - else if(self.health < self.max_health * 0.70) - setmodel(self, "models/onslaught/generator_dmg3.md3"); - else if(self.health < self.max_health * 0.80) - setmodel(self, "models/onslaught/generator_dmg2.md3"); - else if(self.health < self.max_health * 0.90) - setmodel(self, "models/onslaught/generator_dmg1.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); + // only do this if there is power + a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team); + if(!a) + return; - // Throw some flaming gibs on damage, more damage = more chance for gib - if(random() < damage/220) - { - sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - i = random(); - if(i < 0.3) - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE); - else if(i > 0.7) - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE); - else - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE); - } - else + self.health = self.health + self.count; + + self.SendFlags |= CPSF_STATUS; + + if (self.health >= self.max_health) { - // particles on every hit - pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1); + self.health = self.max_health; - self.count = autocvar_g_onslaught_cp_regen * CP_THINKRATE; // slow repair rate from now on ++ self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on + self.think = ons_ControlPoint_Icon_Think; + sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM); + self.owner.iscaptured = TRUE; + self.solid = SOLID_BBOX; - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM); - else - sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM); - } + pointparticles(particleeffectnum(sprintf("%s_cap", Static_Team_ColorName_Lower(self.owner.team))), self.owner.origin, '0 0 0', 1); - //throw some gibs on damage - if(random() < damage/200+0.2) - if(random() < 0.5) - ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE); -} + WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); -// update links after a delay -void onslaught_generator_delayed() -{ - onslaught_updatelinks(); - // now begin normal thinking - self.think = onslaught_generator_think; - self.nextthink = time; -} + if(IS_PLAYER(self.owner.ons_toucher)) + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message); + Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message); + Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message); + PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1); + PlayerTeamScore_AddScore(self.owner.ons_toucher, 10); + } + + self.owner.ons_toucher = world; -string onslaught_generator_waypointsprite_for_team(entity e, float t) -{ - if(t == e.team) - { - if(e.team == NUM_TEAM_1) - return "ons-gen-red"; - else if(e.team == NUM_TEAM_2) - return "ons-gen-blue"; + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; + + self.SendFlags |= CPSF_SETUP; } - if(e.isshielded) - return "ons-gen-shielded"; - if(e.team == NUM_TEAM_1) - return "ons-gen-red"; - else if(e.team == NUM_TEAM_2) - return "ons-gen-blue"; - return ""; + if(self.owner.model != "models/onslaught/controlpoint_pad2.md3") + setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad2.md3"); + + if(random() < 0.9 - self.health / self.max_health) + pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1); } -void onslaught_generator_updatesprite(entity e) +void ons_ControlPoint_Icon_Spawn(entity cp, entity player) { - string s1, s2, s3; - s1 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_1); - s2 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_2); - s3 = onslaught_generator_waypointsprite_for_team(e, -1); - WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3); + entity e = spawn(); + + setsize(e, CPICON_MIN, CPICON_MAX); + setorigin(e, cp.origin + CPICON_OFFSET); + + e.classname = "onslaught_controlpoint_icon"; + e.owner = cp; + e.max_health = autocvar_g_onslaught_cp_health; + e.health = autocvar_g_onslaught_cp_buildhealth; + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_AIM; + e.bot_attack = TRUE; + e.event_damage = ons_ControlPoint_Icon_Damage; + e.team = player.team; + e.colormap = 1024 + (e.team - 1) * 17; - e.count = (e.max_health - e.health) * CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build ++ e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + + sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM); + + cp.goalentity = e; + cp.team = e.team; + cp.colormap = e.colormap; - if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) - { - e.lastteam = e.team + 2; - e.lastshielded = e.isshielded; - if(e.lastshielded) - { - if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); - } - else - { - if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - } + pointparticles(particleeffectnum(sprintf("%sflag_touch", Static_Team_ColorName_Lower(player.team))), e.origin, '0 0 0', 1); + - WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / CP_THINKRATE)); ++ WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); + cp.sprite.SendFlags |= 16; + + onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); } -string onslaught_controlpoint_waypointsprite_for_team(entity e, float t) +string ons_ControlPoint_Waypoint(entity e) { float a; - if(t != -1) + if(e.team) { - a = onslaught_controlpoint_attackable(e, t); - if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW - { - if(e.team == NUM_TEAM_1) - return "ons-cp-atck-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-atck-blue"; - else - return "ons-cp-atck-neut"; - } - else if(a == -2) // DEFEND THIS ONE NOW - { - if(e.team == NUM_TEAM_1) - return "ons-cp-dfnd-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-dfnd-blue"; - } - else if(e.team == t || a == -1 || a == 1) // own point, or fire at it - { - if(e.team == NUM_TEAM_1) - return "ons-cp-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-blue"; - } - else if(a == 2) // touch it - return "ons-cp-neut"; + a = ons_ControlPoint_Attackable(e, e.team); + + if(a == -2) { return "ons-cp-dfnd"; } // defend now + if(a == -1 || a == 1 || a == 2) { return "ons-cp"; } // touch + if(a == 3 || a == 4) { return "ons-cp-atck"; } // attack } else - { - if(e.team == NUM_TEAM_1) - return "ons-cp-red"; - else if(e.team == NUM_TEAM_2) - return "ons-cp-blue"; - else - return "ons-cp-neut"; - } + return "ons-cp"; + return ""; } @@@ -672,1366 -838,736 +672,1373 @@@ void ons_ControlPoint_UpdateSprite(enti } } -void onslaught_generator_reset() +void ons_ControlPoint_Touch() { - self.team = self.team_saved; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; - self.iscaptured = TRUE; - self.islinked = TRUE; - self.isshielded = TRUE; - self.enemy.solid = SOLID_NOT; - self.think = onslaught_generator_delayed; - self.nextthink = time + 0.2; - setmodel(self, "models/onslaught/generator.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - - if(!self.noalign) + entity toucher = other; + float attackable; + + if((toucher.vehicle_flags & VHF_ISVEHICLE) && toucher.owner) + if(autocvar_g_onslaught_allow_vehicle_touch) + toucher = toucher.owner; + else + return; + + if(!IS_PLAYER(toucher)) { return; } ++ if(toucher.frozen) { return; } + if(toucher.deadflag != DEAD_NO) { return; } + + if ( SAME_TEAM(self,toucher) ) + if ( self.iscaptured ) { - setorigin(self, self.origin + '0 0 20'); - droptofloor(); + if(time <= toucher.teleport_antispam) + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); } + + attackable = ons_ControlPoint_Attackable(self, toucher.team); + if(attackable != 2 && attackable != 4) + return; + // we've verified that this player has a legitimate claim to this point, + // so start building the captured point icon (which only captures this + // point if it successfully builds without being destroyed first) + ons_ControlPoint_Icon_Spawn(self, toucher); + + self.ons_toucher = toucher; - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); + onslaught_updatelinks(); } -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) - Base generator. - - spawnfunc_onslaught_link entities can target this. - -keys: -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. - */ -void spawnfunc_onslaught_generator() +void ons_ControlPoint_Think() { - self.nextthink = time + CP_THINKRATE; - if (!g_onslaught) - { - remove(self); - return; - } - - //entity e; - precache_model("models/onslaught/generator.md3"); - precache_model("models/onslaught/generator_shield.md3"); - precache_model("models/onslaught/generator_dmg1.md3"); - precache_model("models/onslaught/generator_dmg2.md3"); - precache_model("models/onslaught/generator_dmg3.md3"); - precache_model("models/onslaught/generator_dmg4.md3"); - precache_model("models/onslaught/generator_dmg5.md3"); - precache_model("models/onslaught/generator_dmg6.md3"); - precache_model("models/onslaught/generator_dmg7.md3"); - precache_model("models/onslaught/generator_dmg8.md3"); - precache_model("models/onslaught/generator_dmg9.md3"); - precache_model("models/onslaught/generator_dead.md3"); - precache_model("models/onslaught/shockwave.md3"); - precache_model("models/onslaught/shockwavetransring.md3"); - precache_model("models/onslaught/gen_gib1.md3"); - precache_model("models/onslaught/gen_gib2.md3"); - precache_model("models/onslaught/gen_gib3.md3"); - precache_model("models/onslaught/ons_ray.md3"); - precache_sound("onslaught/generator_decay.wav"); - precache_sound("weapons/grenade_impact.wav"); - precache_sound("weapons/rocket_impact.wav"); - precache_sound("onslaught/generator_underattack.wav"); - precache_sound("onslaught/shockwave.wav"); - precache_sound("onslaught/ons_hit1.wav"); - precache_sound("onslaught/ons_hit2.wav"); - precache_sound("onslaught/electricity_explode.wav"); - if (!self.team) - objerror("team must be set"); - - if(self.team == NUM_TEAM_1) - ons_red_generator = self; ++ self.nextthink = time + ONS_CP_THINKRATE; + CSQCMODEL_AUTOUPDATE(); +} - if(self.team == NUM_TEAM_2) - ons_blue_generator = self; +void ons_ControlPoint_Reset() +{ + if(self.goalentity) + remove(self.goalentity); - self.team_saved = self.team; - self.colormap = 1024 + (self.team - 1) * 17; - self.solid = SOLID_BBOX; - self.movetype = MOVETYPE_NONE; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - setmodel(self, "models/onslaught/generator.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - setorigin(self, self.origin); - self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; - self.event_damage = onslaught_generator_damage; - self.iscaptured = TRUE; - self.islinked = TRUE; + self.goalentity = world; + self.team = 0; + self.colormap = 1024; + self.iscaptured = FALSE; + self.islinked = FALSE; self.isshielded = TRUE; - // helper entity that create fx when generator is damaged - onslaught_generator_damage_spawn(self); - // spawn shield model which indicates whether this can be damaged - self.enemy = spawn(); - setattachment(self.enemy , self, ""); - self.enemy.classname = "onslaught_generator_shield"; - self.enemy.solid = SOLID_NOT; - self.enemy.movetype = MOVETYPE_NONE; - self.enemy.effects = EF_ADDITIVE; - setmodel(self.enemy, "models/onslaught/generator_shield.md3"); - //setorigin(e, self.origin); - self.enemy.colormap = self.colormap; - self.enemy.team = self.team; - //self.think = onslaught_generator_delayed; - //self.nextthink = time + 0.2; - InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST); - - WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0'); - WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY); - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); + self.think = ons_ControlPoint_Think; + self.ons_toucher = world; - self.nextthink = time + CP_THINKRATE; ++ self.nextthink = time + ONS_CP_THINKRATE; + setmodel_fixsize(self, "models/onslaught/controlpoint_pad.md3"); - waypoint_spawnforitem(self); + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); onslaught_updatelinks(); - self.reset = onslaught_generator_reset; + activator = self; + SUB_UseTargets(); // to reset the structures, playerspawns etc. + + CSQCMODEL_AUTOUPDATE(); } -.float waslinked; -.float cp_bob_spd; -.vector cp_origin, cp_bob_origin, cp_bob_dmg; +void ons_DelayedControlPoint_Setup(void) +{ + onslaught_updatelinks(); + + // captureshield setup + ons_CaptureShield_Spawn(self, FALSE); -float ons_notification_time_team1; -float ons_notification_time_team2; + CSQCMODEL_AUTOINIT(); +} -void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +void ons_ControlPoint_Setup(entity cp) { - entity oself; - float nag; + // declarations + self = cp; // for later usage with droptofloor() + + // main setup + cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist + ons_worldcplist = cp; + + cp.netname = "Control point"; + cp.team = 0; + cp.solid = SOLID_BBOX; + cp.movetype = MOVETYPE_NONE; + cp.touch = ons_ControlPoint_Touch; + cp.think = ons_ControlPoint_Think; - cp.nextthink = time + CP_THINKRATE; ++ cp.nextthink = time + ONS_CP_THINKRATE; + cp.reset = ons_ControlPoint_Reset; + cp.colormap = 1024; + cp.iscaptured = FALSE; + cp.islinked = FALSE; + cp.isshielded = TRUE; + + if(cp.message == "") { cp.message = "a"; } - if (damage <= 0) - return; - if (self.owner.isshielded) + // precache - TODO: clean up! + precache_model("models/onslaught/controlpoint_pad.md3"); + precache_model("models/onslaught/controlpoint_pad2.md3"); + precache_model("models/onslaught/controlpoint_shield.md3"); + precache_model("models/onslaught/controlpoint_icon.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg1.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg2.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg3.md3"); + precache_model("models/onslaught/controlpoint_icon_gib1.md3"); + precache_model("models/onslaught/controlpoint_icon_gib2.md3"); + precache_model("models/onslaught/controlpoint_icon_gib4.md3"); + precache_sound("onslaught/controlpoint_build.wav"); + precache_sound("onslaught/controlpoint_built.wav"); + precache_sound("weapons/grenade_impact.wav"); + precache_sound("onslaught/damageblockedbyshield.wav"); + precache_sound("onslaught/controlpoint_underattack.wav"); + precache_sound("onslaught/ons_spark1.wav"); + precache_sound("onslaught/ons_spark2.wav"); + + // appearence + setmodel_fixsize(cp, "models/onslaught/controlpoint_pad.md3"); + + // control point placement + if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, "onslaught/damageblockedbyshield.wav"); - self.pain_finished = time + 1; - } - return; + cp.noalign = TRUE; + cp.movetype = MOVETYPE_NONE; } - - if (IS_PLAYER(attacker)) + else // drop to floor, automatically find a platform and set that as spawn origin { - nag = FALSE; - if(self.team == NUM_TEAM_1) - { - if(time - ons_notification_time_team1 > 10) - { - nag = TRUE; - ons_notification_time_team1 = time; - } - } - else if(self.team == NUM_TEAM_2) + setorigin(cp, cp.origin + '0 0 20'); + cp.noalign = FALSE; + self = cp; + droptofloor(); + cp.movetype = MOVETYPE_TOSS; + } + + // waypointsprites + WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0'); + WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); + + InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); +} + + +// ========================= +// Main Generator Functions +// ========================= + +string ons_Generator_Waypoint(entity e) +{ + if(e.isshielded) + return "ons-gen-shielded"; + return "ons-gen"; +} + +void ons_Generator_UpdateSprite(entity e) +{ + string s1; + s1 = ons_Generator_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) + { + e.lastteam = e.team + 2; + e.lastshielded = e.isshielded; + if(e.lastshielded) { - if(time - ons_notification_time_team2 > 10) - { - nag = TRUE; - ons_notification_time_team2 = time; - } + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); } else - nag = TRUE; - - if(nag) - play2team(self.team, "onslaught/controlpoint_underattack.wav"); + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); } +} + +void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(damage <= 0) { return; } + if(warmup_stage || gameover) { return; } + if(!round_handler_IsRoundStarted()) { return; } + if (attacker != self) + { + if (self.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, "onslaught/damageblockedbyshield.wav"); + attacker.typehitsound += 1; + self.pain_finished = time + 1; + } + return; + } + if (time > self.pain_finished) + { + self.pain_finished = time + 10; + entity head; + FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); } + play2team(self.team, "onslaught/generator_underattack.wav"); + } + } self.health = self.health - damage; - if(self.owner.iscaptured) - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - else - WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime)); - self.pain_finished = time + 1; - self.punchangle = (2 * randomvec() - '1 1 1') * 45; - self.cp_bob_dmg_z = (2 * random() - 1) * 15; - // colormod flash when shot - self.colormod = '2 2 2'; - // particles on every hit - pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1); - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM); + WaypointSprite_UpdateHealth(self.sprite, self.health); + // choose an animation frame based on health + self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); + // see if the generator is still functional, or dying + if (self.health > 0) + { + self.lasthealth = self.health; + } else - sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM); - - if (self.health < 0) { - sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + if (attacker == self) + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_)); + else { - string t; - t = Team_ColoredFullName(attacker.team); - bprint(Team_ColoredFullName(self.team), " ", self.message, " control point destroyed by ", t, "\n"); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); - ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_)); + PlayerScore_Add(attacker, SP_SCORE, 100); } - self.owner.goalentity = world; - self.owner.islinked = FALSE; - self.owner.iscaptured = FALSE; - self.owner.team = 0; - self.owner.colormap = 1024; + self.iscaptured = FALSE; + self.islinked = FALSE; + self.isshielded = FALSE; + self.takedamage = DAMAGE_NO; // can't be hurt anymore + self.event_damage = func_null; // won't do anything if hurt + self.count = 0; // reset counter + self.think = func_null; + self.nextthink = 0; + //self.think(); // do the first explosion now - WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + WaypointSprite_Ping(self.sprite); + //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor onslaught_updatelinks(); + } - // Use targets now (somebody make sure this is in the right place..) - oself = self; - self = self.owner; - activator = self; - SUB_UseTargets (); - self = oself; - - - self.owner.waslinked = self.owner.islinked; - if(self.owner.model != "models/onslaught/controlpoint_pad.md3") - setmodel(self.owner, "models/onslaught/controlpoint_pad.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); + // Throw some flaming gibs on damage, more damage = more chance for gib + if(random() < damage/220) + { + sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + } + else + { + // particles on every hit + pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1); - remove(self); + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM); + else + sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM); } + + self.SendFlags |= GSF_STATUS; } -void onslaught_controlpoint_icon_think() +void ons_GeneratorThink() { - entity oself; - self.nextthink = time + sys_frametime; - - if(autocvar_g_onslaught_cp_proxydecap) + entity e; + self.nextthink = time + GEN_THINKRATE; + if (!gameover) { - float _enemy_count = 0; - float _friendly_count = 0; - float _dist; - entity _player; - - FOR_EACH_PLAYER(_player) + if(!self.isshielded && self.wait < time) { - if(!_player.deadflag) + self.wait = time + 5; + FOR_EACH_REALPLAYER(e) { - _dist = vlen(_player.origin - self.origin); - if(_dist < autocvar_g_onslaught_cp_proxydecap_distance) - { - if(_player.team == self.team) - ++_friendly_count; - else - ++_enemy_count; + if(SAME_TEAM(e, self)) + { + Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); + soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE); // FIXME: unique sound? } + else + Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_)); } } + } +} + +void ons_GeneratorReset() +{ + self.team = self.team_saved; + self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; + self.takedamage = DAMAGE_AIM; + self.bot_attack = TRUE; + self.iscaptured = TRUE; + self.islinked = TRUE; + self.isshielded = TRUE; + self.event_damage = ons_GeneratorDamage; + self.think = ons_GeneratorThink; + self.nextthink = time + GEN_THINKRATE; + + Net_LinkEntity(self, FALSE, 0, generator_send); + + self.SendFlags = GSF_SETUP; // just incase + self.SendFlags |= GSF_STATUS; - _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime); - _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime); + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); +} - self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); - if(self.health <= 0) - { - onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0'); - return; - } - } +void ons_DelayedGeneratorSetup() +{ + // bot waypoints + waypoint_spawnforitem_force(self, self.origin); + self.nearestwaypointtimeout = 0; // activate waypointing again + self.bot_basewaypoint = self.nearestwaypoint; - if (time > self.pain_finished + 5) + // captureshield setup + ons_CaptureShield_Spawn(self, TRUE); + + onslaught_updatelinks(); + + Net_LinkEntity(self, FALSE, 0, generator_send); +} + + +void onslaught_generator_touch() +{ + if ( IS_PLAYER(other) ) + if ( SAME_TEAM(self,other) ) + if ( self.iscaptured ) { - if(self.health < self.max_health) - { - self.health = self.health + self.count; - if (self.health >= self.max_health) - self.health = self.max_health; - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - } + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT); } - if (self.health < self.max_health * 0.25) - setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3"); - else if (self.health < self.max_health * 0.50) - setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3"); - else if (self.health < self.max_health * 0.75) - setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3"); - else if (self.health < self.max_health * 0.90) - setmodel(self, "models/onslaught/controlpoint_icon.md3"); - // colormod flash when shot - self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); +} - if(self.owner.islinked != self.owner.waslinked) - { - // unteam the spawnpoint if needed - float t; - t = self.owner.team; - if(!self.owner.islinked) - self.owner.team = 0; +void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc +{ + // declarations + float teamnumber = gen.team; + self = gen; // for later usage with droptofloor() + + // main setup + gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist + ons_worldgeneratorlist = gen; + + gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); + gen.classname = "onslaught_generator"; + gen.solid = SOLID_BBOX; + gen.team_saved = teamnumber; + gen.movetype = MOVETYPE_NONE; + gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; + gen.takedamage = DAMAGE_AIM; + gen.bot_attack = TRUE; + gen.event_damage = ons_GeneratorDamage; + gen.reset = ons_GeneratorReset; + gen.think = ons_GeneratorThink; + gen.nextthink = time + GEN_THINKRATE; + gen.iscaptured = TRUE; + gen.islinked = TRUE; + gen.isshielded = TRUE; + gen.touch = onslaught_generator_touch; + + // precache - TODO: clean up! + precache_model("models/onslaught/generator_shield.md3"); + precache_model("models/onslaught/gen_gib1.md3"); + precache_model("models/onslaught/gen_gib2.md3"); + precache_model("models/onslaught/gen_gib3.md3"); + precache_sound("onslaught/generator_decay.wav"); + precache_sound("weapons/grenade_impact.wav"); + precache_sound("weapons/rocket_impact.wav"); + precache_sound("onslaught/generator_underattack.wav"); + precache_sound("onslaught/shockwave.wav"); + precache_sound("onslaught/ons_hit1.wav"); + precache_sound("onslaught/ons_hit2.wav"); + precache_sound("onslaught/generator_underattack.wav"); + + // appearence + // model handled by CSQC + setsize(gen, GENERATOR_MIN, GENERATOR_MAX); + setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); + gen.colormap = 1024 + (teamnumber - 1) * 17; + + // generator placement + self = gen; + droptofloor(); + + // waypointsprites + WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0'); + WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + + InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); +} - oself = self; - self = self.owner; - activator = self; - SUB_UseTargets (); - self = oself; - self.owner.team = t; +// =============== +// Round Handler +// =============== - self.owner.waslinked = self.owner.islinked; +float total_generators, redowned, blueowned, yellowowned, pinkowned; +void Onslaught_count_generators() +{ + entity e; + total_generators = redowned = blueowned = yellowowned = pinkowned = 0; + for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) + { + ++total_generators; + redowned += (e.team == NUM_TEAM_1 && e.health > 0); + blueowned += (e.team == NUM_TEAM_2 && e.health > 0); + yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); + pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); } +} - if (self.punchangle_x > 0) +float Onslaught_GetWinnerTeam() +{ + float winner_team = 0; + if(redowned > 0) + winner_team = NUM_TEAM_1; + if(blueowned > 0) { - self.punchangle_x = self.punchangle_x - 60 * sys_frametime; - if (self.punchangle_x < 0) - self.punchangle_x = 0; + if(winner_team) return 0; + winner_team = NUM_TEAM_2; } - else if (self.punchangle_x < 0) + if(yellowowned > 0) { - self.punchangle_x = self.punchangle_x + 60 * sys_frametime; - if (self.punchangle_x > 0) - self.punchangle_x = 0; + if(winner_team) return 0; + winner_team = NUM_TEAM_3; } - - if (self.punchangle_y > 0) + if(pinkowned > 0) { - self.punchangle_y = self.punchangle_y - 60 * sys_frametime; - if (self.punchangle_y < 0) - self.punchangle_y = 0; + if(winner_team) return 0; + winner_team = NUM_TEAM_4; } - else if (self.punchangle_y < 0) + if(winner_team) + return winner_team; + return -1; // no generators left? +} + +#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) +#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) +float Onslaught_CheckWinner() +{ + entity e; + + if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) { - self.punchangle_y = self.punchangle_y + 60 * sys_frametime; - if (self.punchangle_y > 0) - self.punchangle_y = 0; + ons_stalemate = TRUE; + + if (!wpforenemy_announced) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); + sound(world, CH_INFO, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NONE); + + wpforenemy_announced = TRUE; + } + + entity tmp_entity; // temporary entity + float d; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) + { + // tmp_entity.max_health / 300 gives 5 minutes of overtime. + // control points reduce the overtime duration. + d = 1; + for(e = ons_worldcplist; e; e = e.ons_worldcpnext) + { + if(DIFF_TEAM(e, tmp_entity)) + if(e.islinked) + d = d + 1; + } + + if(autocvar_g_campaign && autocvar__campaign_testrun) + d = d * tmp_entity.max_health; + else + d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); + + Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER, tmp_entity.origin, '0 0 0'); + + tmp_entity.sprite.SendFlags |= 16; + + tmp_entity.ons_overtime_damagedelay = time + 1; + } } + else { wpforenemy_announced = FALSE; ons_stalemate = FALSE; } + + Onslaught_count_generators(); + + if(ONS_OWNED_GENERATORS_OK()) + return 0; + + float winner_team = Onslaught_GetWinnerTeam(); - if (self.punchangle_z > 0) + if(winner_team > 0) { - self.punchangle_z = self.punchangle_z - 60 * sys_frametime; - if (self.punchangle_z < 0) - self.punchangle_z = 0; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); + TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1); } - else if (self.punchangle_z < 0) + else if(winner_team == -1) { - self.punchangle_z = self.punchangle_z + 60 * sys_frametime; - if (self.punchangle_z > 0) - self.punchangle_z = 0; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); } + + ons_stalemate = FALSE; - self.angles_x = self.punchangle_x; - self.angles_y = self.punchangle_y + self.mangle_y; - self.angles_z = self.punchangle_z; - self.mangle_y = self.mangle_y + 45 * sys_frametime; + play2all(sprintf("ctf/%s_capture.wav", Static_Team_ColorName_Lower(winner_team))); + + round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); + + FOR_EACH_PLAYER(e) + { + e.ons_roundlost = TRUE; + e.player_blocked = TRUE; + - self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd)); - self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime; - if(self.cp_bob_dmg_z > 0) - self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * sys_frametime; - else - self.cp_bob_dmg_z = 0; - setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg); ++ nades_Clear(e); + } - + - // damaged fx - if(random() < 0.6 - self.health / self.max_health) + return 1; +} + +float Onslaught_CheckPlayers() +{ + return 1; +} + +void Onslaught_RoundStart() +{ + entity tmp_entity; + FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = FALSE; } + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + tmp_entity.sprite.SendFlags |= 16; + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + tmp_entity.sprite.SendFlags |= 16; +} + + +// ================ +// Bot player logic +// ================ + +// NOTE: LEGACY CODE, needs to be re-written! + +void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius) +{ + entity head; + float t, i, c, needarmor = FALSE, needweapons = FALSE; + + // Needs armor/health? + if(self.health<100) + needarmor = TRUE; + + // Needs weapons? + c = 0; + for(i = WEP_FIRST; i <= WEP_LAST ; ++i) { - pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); + // Find weapon + if(self.weapons & WepSet_FromWeapon(i)) + if(++c>=4) + break; + } - if(random() > 0.8) - sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM); - else if (random() > 0.5) - sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM); + if(c<4) + needweapons = TRUE; + + if(!needweapons && !needarmor) + return; + + ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n")); + ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n")); + + // See what is around + head = findchainfloat(bot_pickup, TRUE); + while (head) + { + // gather health and armor only + if (head.solid) + if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) + if (vlen(head.origin - org) < sradius) + { + t = head.bot_pickupevalfunc(self, head); + if (t > 0) + navigation_routerating(head, t * ratingscale, 500); + } + head = head.chain; } } -void onslaught_controlpoint_icon_buildthink() +void havocbot_role_ons_setrole(entity bot, float role) { - entity oself; - float a; + ons_debug(strcat(bot.netname," switched to ")); + switch(role) + { + case HAVOCBOT_ONS_ROLE_DEFENSE: + ons_debug("defense"); + bot.havocbot_role = havocbot_role_ons_defense; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_ASSISTANT: + ons_debug("assistant"); + bot.havocbot_role = havocbot_role_ons_assistant; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_OFFENSE: + ons_debug("offense"); + bot.havocbot_role = havocbot_role_ons_offense; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; + bot.havocbot_role_timeout = 0; + break; + } + ons_debug("\n"); +} - self.nextthink = time + sys_frametime; +float havocbot_ons_teamcount(entity bot, float role) +{ + float c = 0; + entity head; - // only do this if there is power - a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team); - if(!a) - return; + FOR_EACH_PLAYER(head) + if(SAME_TEAM(head, self)) + if(head.havocbot_role_flags & role) + ++c; - self.health = self.health + self.count; + return c; +} - if (self.health >= self.max_health) +void havocbot_goalrating_ons_controlpoints_attack(float ratingscale) +{ + entity cp, cp1, cp2, best, pl, wp; + float radius, found, bestvalue, c; + + // Filter control points + for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) { - self.health = self.max_health; - self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on - self.think = onslaught_controlpoint_icon_think; - sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM); - bprint(Team_ColoredFullName(self.team), " captured ", self.owner.message, " control point\n"); - self.owner.iscaptured = TRUE; + cp2.wpcost = c = 0; + cp2.wpconsidered = FALSE; - WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + if(cp2.isshielded) + continue; - onslaught_updatelinks(); + // Ignore owned controlpoints + if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team])) + continue; - // Use targets now (somebody make sure this is in the right place..) - oself = self; - self = self.owner; - activator = self; - SUB_UseTargets (); - self = oself; - self.cp_origin = self.origin; - self.cp_bob_origin = '0 0 0.1'; - self.cp_bob_spd = 0; + // Count team mates interested in this control point + // (easier and cleaner than keeping counters per cp and teams) + FOR_EACH_PLAYER(pl) + if(SAME_TEAM(pl, self)) + if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) + if(pl.havocbot_ons_target==cp2) + ++c; + + // NOTE: probably decrease the cost of attackable control points + cp2.wpcost = c; + cp2.wpconsidered = TRUE; } - self.alpha = self.health / self.max_health; - // colormod flash when shot - self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); - if(self.owner.model != "models/onslaught/controlpoint_pad2.md3") - setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); - if(random() < 0.9 - self.health / self.max_health) - pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1); + // We'll consider only the best case + bestvalue = 99999999999; + cp = world; + for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) + { + if (!cp1.wpconsidered) + continue; + + if(cp1.wpcost self.havocbot_role_timeout) + { + havocbot_ons_reset_role(self); return; - // we've verified that this player has a legitimate claim to this point, - // so start building the captured point icon (which only captures this - // point if it successfully builds without being destroyed first) - self.goalentity = e = spawn(); - e.classname = "onslaught_controlpoint_icon"; - e.owner = self; - e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; - e.solid = SOLID_BBOX; - e.movetype = MOVETYPE_NONE; - setmodel(e, "models/onslaught/controlpoint_icon.md3"); - setsize(e, '-32 -32 -32', '32 32 32'); - setorigin(e, self.origin + '0 0 96'); - e.takedamage = DAMAGE_AIM; - e.bot_attack = TRUE; - e.event_damage = onslaught_controlpoint_icon_damage; - e.team = other.team; - e.colormap = 1024 + (e.team - 1) * 17; - e.think = onslaught_controlpoint_icon_buildthink; - e.nextthink = time + sys_frametime; - e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build - sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM); - self.team = e.team; - self.colormap = e.colormap; - WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime)); - onslaught_updatelinks(); + } + + if(self.havocbot_attack_time>time) + return; + + if (self.bot_strategytime < time) + { + navigation_goalrating_start(); + havocbot_goalrating_enemyplayers(20000, self.origin, 650); + if(!havocbot_goalrating_ons_generator_attack(20000)) + havocbot_goalrating_ons_controlpoints_attack(20000); + havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000); + navigation_goalrating_end(); + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + } } -void onslaught_controlpoint_think() +void havocbot_role_ons_assistant() { - self.nextthink = time; - CSQCMODEL_AUTOUPDATE(); + havocbot_ons_reset_role(self); } -void onslaught_controlpoint_reset() +void havocbot_role_ons_defense() { - if(self.goalentity && self.goalentity != world) - remove(self.goalentity); - self.goalentity = world; - self.team = 0; - self.colormap = 1024; - self.iscaptured = FALSE; - self.islinked = FALSE; - self.isshielded = TRUE; - self.enemy.solid = SOLID_NOT; - self.enemy.colormap = self.colormap; - self.think = onslaught_controlpoint_think; - self.enemy.think = func_null; - self.nextthink = time; // don't like func_null :P - setmodel(self, "models/onslaught/controlpoint_pad.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); - - WaypointSprite_UpdateMaxHealth(self.sprite, 0); + havocbot_ons_reset_role(self); +} - onslaught_updatelinks(); +void havocbot_ons_reset_role(entity bot) +{ + entity head; + float c; - activator = self; - SUB_UseTargets(); // to reset the structures, playerspawns etc. - - CSQCMODEL_AUTOUPDATE(); -} + if(self.deadflag != DEAD_NO) + return; -/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) - Control point. Be sure to give this enough clearance so that the shootable part has room to exist + bot.havocbot_ons_target = world; - This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. + // TODO: Defend control points or generator if necessary -keys: -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. -"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. -"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) - */ + // if there is only me on the team switch to offense + c = 0; + FOR_EACH_PLAYER(head) + if(SAME_TEAM(head, self)) + ++c; -void spawnfunc_onslaught_controlpoint() -{ - //entity e; - if (!g_onslaught) + if(c==1) { - remove(self); + havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); return; } - precache_model("models/onslaught/controlpoint_pad.md3"); - precache_model("models/onslaught/controlpoint_pad2.md3"); - precache_model("models/onslaught/controlpoint_shield.md3"); - precache_model("models/onslaught/controlpoint_icon.md3"); - precache_model("models/onslaught/controlpoint_icon_dmg1.md3"); - precache_model("models/onslaught/controlpoint_icon_dmg2.md3"); - precache_model("models/onslaught/controlpoint_icon_dmg3.md3"); - precache_model("models/onslaught/controlpoint_icon_gib1.md3"); - precache_model("models/onslaught/controlpoint_icon_gib2.md3"); - precache_model("models/onslaught/controlpoint_icon_gib4.md3"); - precache_sound("onslaught/controlpoint_build.wav"); - precache_sound("onslaught/controlpoint_built.wav"); - precache_sound("weapons/grenade_impact.wav"); - precache_sound("onslaught/damageblockedbyshield.wav"); - precache_sound("onslaught/controlpoint_underattack.wav"); - precache_sound("onslaught/ons_spark1.wav"); - precache_sound("onslaught/ons_spark2.wav"); - self.solid = SOLID_BBOX; - self.movetype = MOVETYPE_NONE; - setmodel(self, "models/onslaught/controlpoint_pad.md3"); - //setsize(self, '-32 -32 0', '32 32 8'); - if(!self.noalign) - { - setorigin(self, self.origin + '0 0 20'); - droptofloor(); - } - self.touch = onslaught_controlpoint_touch; - self.team = 0; - self.colormap = 1024; - self.iscaptured = FALSE; - self.islinked = FALSE; - self.isshielded = TRUE; + havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); +} - // spawn shield model which indicates whether this can be damaged - self.enemy = spawn(); - self.enemy.classname = "onslaught_controlpoint_shield"; - self.enemy.solid = SOLID_NOT; - self.enemy.movetype = MOVETYPE_NONE; - self.enemy.effects = EF_ADDITIVE; - setmodel(self.enemy , "models/onslaught/controlpoint_shield.md3"); - setattachment(self.enemy , self, ""); - //setsize(e, '-32 -32 0', '32 32 128'); +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + */ +entity ons_Nearest_ControlPoint(vector pos, float max_dist) +{ + entity tmp_entity, closest_target = world; + tmp_entity = findchain(classname, "onslaught_controlpoint"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + tmp_entity = tmp_entity.chain; + } + + return closest_target; +} - //setorigin(e, self.origin); - self.enemy.colormap = self.colormap; +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + * This function only check distances on the XY plane, disregarding Z + */ +entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist) +{ + entity tmp_entity, closest_target = world; + vector delta; + float smallest_distance = 0, distance; + + tmp_entity = findchain(classname, "onslaught_controlpoint"); + while(tmp_entity) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + + tmp_entity = tmp_entity.chain; + } + + return closest_target; +} +/** + * find the number of control points and generators in the same team as self + */ +float ons_Count_SelfControlPoints() +{ + entity tmp_entity; + tmp_entity = findchain(classname, "onslaught_controlpoint"); + float n = 0; + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + n++; + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + n++; + tmp_entity = tmp_entity.chain; + } + return n; +} - waypoint_spawnforitem(self); +/** + * Teleport player to a random position near tele_target + * if tele_effects is true, teleport sound+particles are created + * return FALSE on failure + */ +float ons_Teleport(entity player, entity tele_target, float range, float tele_effects) +{ + if ( !tele_target ) + return FALSE; - self.think = onslaught_controlpoint_think; - self.nextthink = time; + float i; + vector loc; + float theta; + for(i = 0; i < 16; ++i) + { + theta = random() * 2 * M_PI; + loc_y = sin(theta); + loc_x = cos(theta); + loc_z = 0; + loc *= random() * range; + + loc += tele_target.origin + '0 0 128'; + + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + if ( tele_effects ) + { + pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1); + sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM); + } + setorigin(player, loc); + player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); + makevectors(player.angles); + player.fixangle = TRUE; + player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; + + if ( tele_effects ) + pointparticles(particleeffectnum("teleport"), player.origin + v_forward * 32, '0 0 0', 1); + return TRUE; + } + } + } + + return FALSE; +} - WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0'); - WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY); +// ============== +// Hook Functions +// ============== - onslaught_updatelinks(); +MUTATOR_HOOKFUNCTION(ons_ResetMap) +{ + FOR_EACH_PLAYER(self) + { + self.ons_roundlost = FALSE; + self.ons_deathloc = '0 0 0'; + PutClientInServer(); + } + return FALSE; +} - self.reset = onslaught_controlpoint_reset; - - CSQCMODEL_AUTOINIT(); +MUTATOR_HOOKFUNCTION(ons_RemovePlayer) +{ + self.ons_deathloc = '0 0 0'; + return FALSE; } -float onslaught_link_send(entity to, float sendflags) +MUTATOR_HOOKFUNCTION(ons_PlayerSpawn) { - WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK); - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & 1) + if(!round_handler_IsRoundStarted()) { - WriteCoord(MSG_ENTITY, self.goalentity.origin_x); - WriteCoord(MSG_ENTITY, self.goalentity.origin_y); - WriteCoord(MSG_ENTITY, self.goalentity.origin_z); + self.player_blocked = TRUE; + return FALSE; } - if(sendflags & 2) + + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) { - WriteCoord(MSG_ENTITY, self.enemy.origin_x); - WriteCoord(MSG_ENTITY, self.enemy.origin_y); - WriteCoord(MSG_ENTITY, self.enemy.origin_z); + l.sprite.SendFlags |= 16; } - if(sendflags & 4) + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) { - WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16 + l.sprite.SendFlags |= 16; } - return TRUE; -} -void onslaught_link_checkupdate() -{ - // TODO check if the two sides have moved (currently they won't move anyway) - float redpower, bluepower; + if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } - redpower = bluepower = 0; - if(self.goalentity.islinked) + if ( autocvar_g_onslaught_spawn_choose ) + if ( self.ons_spawn_by ) + if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,FALSE) ) { - if(self.goalentity.team == NUM_TEAM_1) - redpower = 1; - else if(self.goalentity.team == NUM_TEAM_2) - bluepower = 1; + self.ons_spawn_by = world; + return FALSE; } - if(self.enemy.islinked) + + if(autocvar_g_onslaught_spawn_at_controlpoints) + if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) { - if(self.enemy.team == NUM_TEAM_1) - redpower = 2; - else if(self.enemy.team == NUM_TEAM_2) - bluepower = 2; - } + float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; + entity tmp_entity, closest_target = world; + vector spawn_loc = self.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return FALSE; } - float cc; - if(redpower == 1 && bluepower == 2) - cc = (NUM_TEAM_1 - 1) * 0x01 + (NUM_TEAM_2 - 1) * 0x10; - else if(redpower == 2 && bluepower == 1) - cc = (NUM_TEAM_1 - 1) * 0x10 + (NUM_TEAM_2 - 1) * 0x01; - else if(redpower) - cc = (NUM_TEAM_1 - 1) * 0x11; - else if(bluepower) - cc = (NUM_TEAM_2 - 1) * 0x11; - else - cc = 0; - - //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " "); - //print("cc=", ftos(cc), "\n"); + if(random_target) { RandomSelection_Init(); } - if(cc != self.clientcolors) - { - self.clientcolors = cc; - self.SendFlags |= 4; + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self)) + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + for(i = 0; i < 10; ++i) + { + loc = closest_target.origin + '0 0 96'; + loc += ('0 1 0' * random()) * 128; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return FALSE; + } + } + } + } + } + + if(autocvar_g_onslaught_spawn_at_generator) + if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_generator_random; + entity tmp_entity, closest_target = world; + vector spawn_loc = self.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return FALSE; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else + { + if(SAME_TEAM(tmp_entity, self)) + if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + for(i = 0; i < 10; ++i) + { + loc = closest_target.origin + '0 0 128'; + loc += ('0 1 0' * random()) * 256; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return FALSE; + } + } + } + } } - self.nextthink = time; + return FALSE; } -void onslaught_link_delayed() +MUTATOR_HOOKFUNCTION(ons_PlayerDies) { - self.goalentity = find(world, targetname, self.target); - self.enemy = find(world, targetname, self.target2); - if (!self.goalentity) - objerror("can not find target\n"); - if (!self.enemy) - objerror("can not find target2\n"); - dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n"); - self.SendFlags |= 3; - self.think = onslaught_link_checkupdate; - self.nextthink = time; + frag_target.ons_deathloc = frag_target.origin; + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( ons_Count_SelfControlPoints() > 1 ) + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); + + return FALSE; } -/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) - Link between control points. +MUTATOR_HOOKFUNCTION(ons_MonsterThink) +{ + entity e = find(world, targetname, self.target); + if (e != world) + self.team = e.team; - This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. + return FALSE; +} -keys: -"target" - first control point. -"target2" - second control point. - */ -void spawnfunc_onslaught_link() +void ons_MonsterSpawn_Delayed() { - if (!g_onslaught) + entity e, own = self.owner; + + if(!own) { remove(self); return; } + + if(own.targetname) { - remove(self); - return; + e = find(world, target, own.targetname); + if(e != world) + { + own.team = e.team; + + activator = e; + own.use(); + } } - if (self.target == "" || self.target2 == "") - objerror("target and target2 must be set\n"); - InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET); - Net_LinkEntity(self, FALSE, 0, onslaught_link_send); + + remove(self); } -MUTATOR_HOOKFUNCTION(ons_BuildMutatorsString) +MUTATOR_HOOKFUNCTION(ons_MonsterSpawn) { - ret_string = strcat(ret_string, ":ONS"); - return 0; -} + entity e = spawn(); + e.owner = self; + InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); -MUTATOR_HOOKFUNCTION(ons_BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Onslaught"); - return 0; + return FALSE; } -MUTATOR_HOOKFUNCTION(ons_Spawn_Score) +void ons_TurretSpawn_Delayed() { + entity e, own = self.owner; - /* - float _neer_home = (random() > 0.5 ? TRUE : FALSE); + if(!own) { remove(self); return; } - RandomSelection_Init(); + if(own.targetname) + { + e = find(world, target, own.targetname); + if(e != world) + { + own.team = e.team; + own.active = ACTIVE_NOT; + + activator = e; + own.use(); + } + } - if(self.team == NUM_TEAM_1) - RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1); + remove(self); +} - if(self.team == NUM_TEAM_2) - RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1); +MUTATOR_HOOKFUNCTION(ons_TurretSpawn) +{ + entity e = spawn(); + e.owner = self; + InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); - entity _cp = findchain(classname, "onslaught_controlpoint"): - while _cp; - { - if(_cp.team == self.team) - RandomSelection_Add(_cp, 0, string_null, 1, 1); + return FALSE; +} - _cp = _cp.chain; - } +MUTATOR_HOOKFUNCTION(ons_BotRoles) +{ + havocbot_ons_reset_role(self); + return TRUE; +} - if(RandomSelection_chosen_ent) +MUTATOR_HOOKFUNCTION(ons_GetTeamCount) +{ + // onslaught is special + entity tmp_entity; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) { - self.tur_head = RandomSelection_chosen_ent; - spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + switch(tmp_entity.team) + { + case NUM_TEAM_1: c1 = 0; break; + case NUM_TEAM_2: c2 = 0; break; + case NUM_TEAM_3: c3 = 0; break; + case NUM_TEAM_4: c4 = 0; break; + } } - else if(self.team == spawn_spot.team) - spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate - */ + return TRUE; +} + +MUTATOR_HOOKFUNCTION(ons_SpectateCopy) +{ + self.ons_roundlost = other.ons_roundlost; // make spectators see it too + return FALSE; +} +MUTATOR_HOOKFUNCTION(ons_SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? + return FALSE; + + if ( cmd_name == "ons_spawn" ) + { + vector pos = self.origin; + if(cmd_argc > 1) + pos_x = stof(argv(1)); + if(cmd_argc > 2) + pos_y = stof(argv(2)); + if(cmd_argc > 3) + pos_z = stof(argv(3)); + + if ( IS_PLAYER(self) ) + { - entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); - - if ( !source_point && self.health > 0 ) - { - sprint(self, "\nYou need to be next to a control point\n"); - return 1; - } - - entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius); - - if ( closest_target == world ) - { - sprint(self, "\nNo control point found\n"); - return 1; - } - - if ( self.health <= 0 ) - { - self.ons_spawn_by = closest_target; - self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; - } - else ++ if ( !self.frozen ) + { - if ( source_point == closest_target ) ++ entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); ++ ++ if ( !source_point && self.health > 0 ) + { - sprint(self, "\nTeleporting to the same point\n"); ++ sprint(self, "\nYou need to be next to a control point\n"); + return 1; + } - - if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,TRUE) ) - sprint(self, "\nUnable to teleport there\n"); ++ ++ ++ entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius); ++ ++ if ( closest_target == world ) ++ { ++ sprint(self, "\nNo control point found\n"); ++ return 1; ++ } ++ ++ if ( self.health <= 0 ) ++ { ++ self.ons_spawn_by = closest_target; ++ self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; ++ } ++ else ++ { ++ if ( source_point == closest_target ) ++ { ++ sprint(self, "\nTeleporting to the same point\n"); ++ return 1; ++ } ++ ++ if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,TRUE) ) ++ sprint(self, "\nUnable to teleport there\n"); ++ } ++ ++ return 1; + } + - //sprint(self, "\nNo teleportation for you\n"); - - return 1; ++ sprint(self, "\nNo teleportation for you\n"); + } + + return 1; + } return 0; } diff --cc qcsrc/server/mutators/gamemode_onslaught.qh index d78dfe5d7,000000000..cb4aeb4cf mode 100644,000000..100644 --- a/qcsrc/server/mutators/gamemode_onslaught.qh +++ b/qcsrc/server/mutators/gamemode_onslaught.qh @@@ -1,94 -1,0 +1,94 @@@ +// these are needed since mutators are compiled last + +#ifdef SVQC + +.entity ons_toucher; // player who touched the control point + +// control point / generator constants - #define CP_THINKRATE 0.2 ++#define ONS_CP_THINKRATE 0.2 +#define GEN_THINKRATE 1 +#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13)) +#define CPGEN_WAYPOINT_OFFSET ('0 0 128') +#define CPICON_OFFSET ('0 0 96') + +// list of generators on the map +entity ons_worldgeneratorlist; +.entity ons_worldgeneratornext; +.entity ons_stalegeneratornext; + +// list of control points on the map +entity ons_worldcplist; +.entity ons_worldcpnext; +.entity ons_stalecpnext; + +// list of links on the map +entity ons_worldlinklist; +.entity ons_worldlinknext; +.entity ons_stalelinknext; + +// definitions +.entity sprite; +.string target2; +.float iscaptured; +.float islinked; +.float isshielded; +.float lasthealth; +.float lastteam; +.float lastshielded; +.float lastcaptured; + +.float waslinked; + +float ons_stalemate; + +.float teleport_antispam; + +.float ons_roundlost; + +// waypoint sprites +.entity bot_basewaypoint; // generator waypointsprite +float wpforenemy_announced; + +.float isgenneighbor[17]; +.float iscpneighbor[17]; +float ons_notification_time[17]; + +.float ons_overtime_damagedelay; + +.vector ons_deathloc; + +.entity ons_spawn_by; + +// declarations for functions used outside gamemode_onslaught.qc +void ons_Generator_UpdateSprite(entity e); +void ons_ControlPoint_UpdateSprite(entity e); +float ons_ControlPoint_Attackable(entity cp, float teamnumber); + +// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet +float ons_captureshield_force; // push force of the shield + +// bot player logic +#define HAVOCBOT_ONS_ROLE_NONE 0 +#define HAVOCBOT_ONS_ROLE_DEFENSE 2 +#define HAVOCBOT_ONS_ROLE_ASSISTANT 4 +#define HAVOCBOT_ONS_ROLE_OFFENSE 8 + +.entity havocbot_ons_target; + +.float havocbot_role_flags; +.float havocbot_attack_time; + +void havocbot_role_ons_defense(); +void havocbot_role_ons_offense(); +void havocbot_role_ons_assistant(); + +void havocbot_ons_reset_role(entity bot); +void havocbot_goalrating_items(float ratingscale, vector org, float sradius); +void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius); + +// score rule declarations +#define ST_ONS_CAPS 1 +#define SP_ONS_CAPS 4 +#define SP_ONS_TAKES 6 + +#endif diff --cc qcsrc/server/mutators/mutators_include.qh index 000000000,c869ab696..ef2809ae2 mode 000000,100644..100644 --- a/qcsrc/server/mutators/mutators_include.qh +++ b/qcsrc/server/mutators/mutators_include.qh @@@ -1,0 -1,18 +1,19 @@@ + #include "base.qh" + #include "mutators.qh" + #include "gamemode_assault.qh" + #include "gamemode_ca.qh" + #include "gamemode_ctf.qh" + #include "gamemode_domination.qh" + #include "gamemode_keyhunt.qh" + #include "gamemode_keepaway.qh" + #include "gamemode_nexball.qh" + #include "gamemode_lms.qh" + #include "gamemode_invasion.qh" + #include "gamemode_race.qh" + #include "gamemode_cts.qh" ++#include "gamemode_onslaught.qh" + + #include "mutator_dodging.qh" + #include "mutator_overkill.qh" + #include "mutator_nades.qh" + #include "mutator_buffs.qh"