1 #using scripts\codescripts\struct;
3 #using scripts\shared\aat_shared;
4 #using scripts\shared\array_shared;
5 #using scripts\shared\callbacks_shared;
6 #using scripts\shared\flag_shared;
7 #using scripts\shared\spawner_shared;
8 #using scripts\shared\system_shared;
9 #using scripts\shared\trigger_shared;
10 #using scripts\shared\util_shared;
11 #using scripts\shared\vehicle_shared;
12 #using scripts\shared\vehicles\_parasite;
13 #using scripts\shared\clientfield_shared;
14 #using scripts\shared\visionset_mgr_shared;
15 #using scripts\shared\ai_shared;
16 #using scripts\shared\vehicles\_parasite;
18 #insert scripts\shared\shared.gsh;
19 #insert scripts\shared\weapons\grapple.gsh;
21 #using scripts\zm\_util;
22 #using scripts\zm\_zm;
23 #using scripts\zm\_zm_audio;
24 #using scripts\zm\_zm_net;
25 #using scripts\zm\_zm_powerups;
26 #using scripts\zm\_zm_score;
27 #using scripts\zm\_zm_spawner;
28 #using scripts\zm\_zm_stats;
29 #using scripts\zm\_zm_utility;
30 #using scripts\zm\zm_zod_idgun_quest;
32 #using scripts\shared\ai\zombie_utility;
34 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
35 #insert scripts\zm\_zm_utility.gsh;
36 #insert scripts\shared\aat_zm.gsh;
37 #insert scripts\shared\version.gsh;
38 #insert scripts\zm\_zm_ai_wasp.gsh;
39 #insert scripts\zm\zm_zod_craftables.gsh;
41 #precache( "fx", "zombie/fx_parasite_spawn_buildup_zod_zmb" );
43 #define N_MAX_WASPS 16 // Max number that can be alive at any one time (mainly limited by networking concerns)
44 #define N_MAX_WASPS_PER_PLAYER 5 // Max alive per player in the game
46 #define N_SWARM_SIZE 1
49 #define N_NUM_WASPS_PER_ROUND 10
50 #define N_WASP_PLAYER_SCALAR 0.75
52 #define N_SPAWN_HEIGHT_MIN 60 // Minimum ground height to spawn at
53 #define N_WASP_HEALTH_INCREASE 50 // Amount to increase Wasp health
54 #define N_WASP_HEALTH_MAX 1600 // Maximum health
55 #define N_WASP_KILL_POINTS 70 // Points per kill
58 #namespace zm_ai_wasp;
62 level.wasp_enabled =
true;
63 level.wasp_rounds_enabled =
false;
64 level.wasp_round_count = 1;
66 level.wasp_spawners = [];
68 level.a_wasp_priority_targets = [];
73 level.melee_range_sav = GetDvarString(
"ai_meleeRange" );
74 level.melee_width_sav = GetDvarString(
"ai_meleeWidth" );
75 level.melee_height_sav = GetDvarString(
"ai_meleeHeight" );
85 level._effect[
"lightning_wasp_spawn" ] =
"zombie/fx_parasite_spawn_buildup_zod_zmb";
107 level.wasp_rounds_enabled =
true;
109 if( !isdefined( level.wasp_round_track_override ) )
114 level thread [[level.wasp_round_track_override]]();
120 level.wasp_spawners = getEntArray(
"zombie_wasp_spawner",
"script_noteworthy" );
121 later_wasp = getentarray(
"later_round_wasp_spawners",
"script_noteworthy" );
122 level.wasp_spawners = ArrayCombine( level.wasp_spawners, later_wasp,
true,
false );
124 if( level.wasp_spawners.size == 0 )
129 for( i = 0; i < level.wasp_spawners.size; i++ )
133 level.wasp_spawners[i].is_enabled =
false;
137 level.wasp_spawners[i].is_enabled =
true;
138 level.wasp_spawners[i].script_forcespawn =
true;
142 assert( level.wasp_spawners.size > 0 );
143 level.wasp_health = 100;
145 vehicle::add_main_callback(
"spawner_bo3_parasite_enemy_tool", &
wasp_init );
150 wasps = GetEntArray(
"zombie_wasp",
"targetname" );
151 num_alive_wasps = wasps.size;
152 foreach( wasp
in wasps )
154 if( !IsAlive( wasp ) )
159 return num_alive_wasps;
164 level endon(
"intermission" );
165 level endon(
"wasp_round" );
167 level.wasp_targets = level.players;
168 for( i = 0 ; i < level.wasp_targets.size; i++ )
170 level.wasp_targets[i].hunted_by = 0;
173 level endon(
"restart_round" );
174 level endon(
"kill_round" );
177 if ( GetDvarInt(
"zombie_cheat" ) == 2 || GetDvarInt(
"zombie_cheat" ) >= 4 )
183 if( level.intermission )
192 if ( level.players.size > 1 )
203 if( GetDvarString(
"force_wasp" ) !=
"" && GetDvarInt(
"force_wasp" ) > 0 )
205 level.zombie_total = GetDvarInt(
"force_wasp" );
206 SetDvar(
"force_wasp", 0 );
216 PlaySoundAtPosition(
"vox_zmba_event_waspstart_0", ( 0, 0, 0 ) );
225 level endon(
"last_ai_down" );
231 while( level.zombie_total > 0 )
233 if(
IS_TRUE( level.bzm_worldPaused ) )
238 if( isdefined(level.zm_mixed_wasp_raps_spawning) )
240 [[level.zm_mixed_wasp_raps_spawning]]();
257 b_swarm_spawned =
false;
259 while ( !b_swarm_spawned )
268 spawn_point = undefined;
270 while ( !isdefined( spawn_point ) )
273 spawn_enemy = favorite_enemy;
274 if( !IsDefined( spawn_enemy ) )
277 spawn_enemy = GetPlayers()[0];
280 if ( isdefined( level.wasp_spawn_func ) )
282 spawn_point = [[ level.wasp_spawn_func ]]( spawn_enemy );
290 if ( !isdefined( spawn_point ) )
297 v_spawn_origin = spawn_point.origin;
304 queryResult = PositionQuery_Source_Navigation( v_spawn_origin, 0, 80, 80, 15,
"navvolume_small" );
305 a_points = array::randomize( queryResult.data );
309 a_spawn_origins = [];
313 foreach ( point
in a_points )
315 if ( BulletTracePassed( point.origin, spawn_point.origin,
false, spawn_enemy ) )
330 while ( n_spawn < N_SWARM_SIZE && level.zombie_total > 0 )
332 for ( i = a_spawn_origins.size - 1; i >= 0; i-- )
334 v_origin = a_spawn_origins[ i ];
336 level.wasp_spawners[ 0 ].origin = v_origin;
340 if ( isdefined( ai ) )
344 ArrayRemoveIndex( a_spawn_origins, i );
346 if( isdefined(level.zm_wasp_spawn_callback) )
348 ai thread [[level.zm_wasp_spawn_callback]]();
352 level.zombie_total--;
361 b_swarm_spawned =
true;
371 foreach ( player
in level.players )
380 if ( IsDefined(
self ) && IsDefined(
self.hud_damagefeedback ) )
382 self.hud_damagefeedback SetShader(
"damage_feedback", 24, 48 );
383 self.hud_damagefeedback.alpha = 1;
384 self.hud_damagefeedback FadeOverTime(1);
385 self.hud_damagefeedback.alpha = 0;
389 function waspDamage( inflictor, attacker,
damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex )
391 if( isdefined(attacker) )
406 if( b_wasp_count_at_max || b_wasp_count_per_player_at_max || !( level
flag::get(
"spawn_zombies" ) ) )
416 level waittill(
"last_ai_down", e_wasp );
420 if( isdefined(level.zm_override_ai_aftermath_powerup_drop) )
422 [[level.zm_override_ai_aftermath_powerup_drop]]( e_wasp, level.last_ai_origin );
426 if( isdefined( level.last_ai_origin ) )
428 enemy = e_wasp.favoriteenemy;
429 if (!IsDefined(enemy))
430 enemy = array::random(level.players);
437 level.sndMusicSpecialRound =
false;
454 if( isdefined( current_zone ) )
456 const n_ground_offset = 20;
458 v_start = e_parasite_drop.origin;
460 e_closest_player = ArrayGetClosest( v_start, level.activeplayers );
461 if( isdefined( e_closest_player ) )
463 v_target = e_closest_player.origin + (0, 0, n_ground_offset);
469 n_distance_to_target = Distance( v_start, v_target );
470 v_dir = VectorNormalize( v_target - v_start );
473 n_distance_moved = 0;
474 v_position = v_start;
475 while( n_distance_moved <= n_distance_to_target )
477 v_position += (v_dir * n_step);
483 n_height_diff = abs( v_target[2] - v_position[2] );
484 if( n_height_diff < 60 )
490 n_distance_moved += n_step;
494 trace = bullettrace( v_position, v_position + (0,0,-256 ),
false, undefined );
495 v_ground_position =
trace[
"position"];
496 if( isdefined(v_ground_position) )
498 v_position = ( v_position[0], v_position[1], v_ground_position[2] + n_ground_offset );
502 n_flight_time = Distance( v_start, v_position ) / 100;
503 if( n_flight_time > 4.0 )
507 e_parasite_drop MoveTo( v_position, n_flight_time );
512 v_nav_check = GetClosestPointOnNavMesh( e_parasite_drop.origin, 2000, 32 );
530 ai SetInvisibleToAll();
532 if ( isdefined( origin ) )
538 v_origin = ai.origin;
541 if( should_spawn_fx )
543 PlayFx( level._effect[
"lightning_wasp_spawn"], v_origin );
550 Earthquake( 0.3, 0.5, v_origin, 256);
555 if ( IsDefined(ai.favoriteenemy) )
556 angle = VectorToAngles( ai.favoriteenemy.origin - v_origin );
559 angles = ( ai.angles[0], angle[1], ai.angles[2] );
563 ai.origin = v_origin;
566 assert( isdefined( ai ),
"Ent isn't defined." );
567 assert( IsAlive( ai ),
"Ent is dead." );
571 if( isdefined( level._wasp_death_cb ) )
576 ai SetVisibleToAll();
578 ai notify(
"visible" );
586 if( !isdefined(level.enemy_wasp_global_locations) )
588 level.enemy_wasp_global_locations = [];
589 keys = GetArrayKeys( level.zones );
590 for( i=0; i<keys.size; i++ )
592 zone = level.zones[keys[i]];
595 foreach( loc
in zone.a_locs[
"wasp_location" ] )
597 ARRAY_ADD( level.enemy_wasp_global_locations, loc );
606 closest_distance_squared = DistanceSquared( level.enemy_wasp_global_locations[index_to_use].origin, favorite_enemy.origin );
607 for( i = 0; i < level.enemy_wasp_global_locations.size; i++ )
609 if( level.enemy_wasp_global_locations[i].is_enabled )
611 dist_squared = DistanceSquared( level.enemy_wasp_global_locations[i].origin, favorite_enemy.origin );
612 if( dist_squared<closest_distance_squared )
615 closest_distance_squared = dist_squared;
619 return level.enemy_wasp_global_locations[index_to_use];
622 #define WASP_SPAWN_DIST_MIN 400
623 #define WASP_SPAWN_DIST_MAX 600
627 if ( !GetDvarInt(
"zm_wasp_open_spawning", 0 ) )
629 wasp_locs = level.zm_loc_types[
"wasp_location" ];
631 if ( wasp_locs.size == 0 )
639 if ( isdefined( level.old_wasp_spawn ) )
641 dist_squared = DistanceSquared( level.old_wasp_spawn.origin, favorite_enemy.origin );
644 return level.old_wasp_spawn;
649 foreach ( loc
in wasp_locs )
651 dist_squared = DistanceSquared( loc.origin, favorite_enemy.origin );
654 level.old_wasp_spawn = loc;
662 const spawn_height_min = 40;
663 const spawn_height_max = 100;
664 const spawn_dist_min = 300;
666 switch( level.players.size )
669 spawn_dist_max = 600;
673 spawn_dist_max = 700;
677 spawn_dist_max = 900;
682 spawn_dist_max = 1200;
686 queryResult = PositionQuery_Source_Navigation(
687 favorite_enemy.origin + ( 0, 0, RandomIntRange( spawn_height_min, spawn_height_max ) ),
694 a_points = array::randomize( queryResult.data );
696 foreach ( point
in a_points )
698 if ( BulletTracePassed( point.origin, favorite_enemy.origin,
false, favorite_enemy ) )
700 level.old_wasp_spawn = point;
712 if( level.a_wasp_priority_targets.size > 0 )
714 e_enemy = level.a_wasp_priority_targets[0];
715 if( isdefined(e_enemy) )
717 ArrayRemoveValue( level.a_wasp_priority_targets, e_enemy );
723 if ( isdefined( level.fn_custom_wasp_favourate_enemy ) )
725 e_enemy = [[ level.fn_custom_wasp_favourate_enemy ]]();
737 players = getplayers();
750 level endon(
"restart_round" );
751 level endon(
"kill_round" );
763 level.wasp_round_count = 1;
769 level.next_wasp_round = level.round_number + RandomIntRange( 4, 6 );
772 old_spawn_func = level.round_spawn_func;
773 old_wait_func = level.round_wait_func;
777 level waittill (
"between_round_over" );
780 if( GetDvarInt(
"force_wasp" ) > 0 )
782 level.next_wasp_round = level.round_number;
786 if ( level.round_number == level.next_wasp_round )
788 level.sndMusicSpecialRound =
true;
789 old_spawn_func = level.round_spawn_func;
790 old_wait_func = level.round_wait_func;
796 if( isdefined(level.zm_custom_get_next_wasp_round) )
798 level.next_wasp_round = [[level.zm_custom_get_next_wasp_round]]();
803 level.next_wasp_round = 5 + (level.wasp_round_count * 10) + RandomIntRange( -1, 1 );
807 GetPlayers()[0] iprintln(
"Next wasp round: " + level.next_wasp_round );
813 level.round_spawn_func = old_spawn_func;
814 level.round_wait_func = old_wait_func;
815 level.wasp_round_count += 1;
826 if(!isdefined (level.waspround_nomusic))
828 level.waspround_nomusic = 0;
830 level.waspround_nomusic = 1;
831 level notify(
"wasp_round_starting" );
834 if(isdefined(level.wasp_melee_range))
836 SetDvar(
"ai_meleeRange", level.wasp_melee_range );
840 SetDvar(
"ai_meleeRange", 100 );
850 if(!isdefined (level.waspround_nomusic))
852 level.waspround_nomusic = 0;
854 level.waspround_nomusic = 0;
855 level notify(
"wasp_round_ending" );
857 SetDvar(
"ai_meleeRange", level.melee_range_sav );
858 SetDvar(
"ai_meleeWidth", level.melee_width_sav );
859 SetDvar(
"ai_meleeHeight", level.melee_height_sav );
865 self playlocalsound(
"zmb_wasp_round_start" );
870 players = getplayers();
871 num = RandomIntRange(0,players.size);
877 self.targetname =
"zombie_wasp";
878 self.script_noteworthy = undefined;
879 self.animname =
"zombie_wasp";
880 self.ignoreall =
true;
881 self.ignoreme =
true;
882 self.allowdeath =
true;
883 self.allowpain =
false;
885 self.is_zombie =
true;
888 self.head_gibbed =
false;
889 self.default_goalheight = 40;
890 self.ignore_inert =
true;
891 self.no_eye_glow =
true;
893 self.lightning_chain_immune =
true;
895 self.holdfire =
false;
899 self.grenadeawareness = 0;
900 self.badplaceawareness = 0;
902 self.ignoreSuppression =
true;
903 self.suppressionThreshold = 1;
904 self.noDodgeMove =
true;
905 self.dontShootWhileMoving =
true;
906 self.pathenemylookahead = 0;
908 self.badplaceawareness = 0;
909 self.chatInitialized =
false;
910 self.missingLegs =
false;
912 self.teslafxtag =
"tag_origin";
915 self SetGrapplableType(
self.grapple_type );
917 self.team = level.zombie_team;
929 health_multiplier = 1.0;
930 if ( GetDvarString(
"scr_wasp_health_walk_multiplier" ) !=
"" )
932 health_multiplier = GetDvarFloat(
"scr_wasp_health_walk_multiplier" );
935 self.maxhealth = int( level.wasp_health * health_multiplier );
936 if( IsDefined(level.a_zombie_respawn_health[
self.archetype ] ) && level.a_zombie_respawn_health[
self.archetype ].size > 0 )
938 self.health = level.a_zombie_respawn_health[
self.archetype ][0];
939 ArrayRemoveValue(level.a_zombie_respawn_health[
self.archetype ], level.a_zombie_respawn_health[
self.archetype ][0]);
943 self.health = int( level.wasp_health * health_multiplier );
949 self SetInvisibleToAll();
961 if ( isdefined(level.achievement_monitor_func) )
963 self [[level.achievement_monitor_func]]();
970 self endon(
"death" );
972 n_damage = Int(
self.maxhealth * 0.5 );
973 self DoDamage( n_damage,
self.origin, e_player );
976 #define N_WASP_NOT_MOVED_TIMEOUT 20
977 #define N_WASP_MAX_LIFE_TIMEOUT 150
978 #define N_WASP_HAS_MOVE_DIST 100
984 self endon(
"death" );
986 n_wasp_created_time = GetTime();
988 n_check_time = n_wasp_created_time;
989 v_check_position =
self.origin;
993 n_current_time = GetTime();
995 if(
IS_TRUE( level.bzm_worldPaused ) )
997 n_check_time = n_current_time;
1003 n_dist = Distance( v_check_position,
self.origin );
1006 n_check_time = n_current_time;
1007 v_check_position =
self.origin;
1013 n_delta_time = ( n_current_time - n_check_time ) /
N_MSEC;
1021 n_delta_time = ( n_current_time - n_wasp_created_time ) /
N_MSEC;
1030 self DoDamage(
self.health + 100,
self.origin );
1035 self waittill(
"death", attacker );
1040 if( ( !isdefined(level.zm_ai_round_over) || [[level.zm_ai_round_over]]() ) )
1042 level.last_ai_origin =
self.origin;
1043 level notify(
"last_ai_down",
self );
1048 if( IsPlayer( attacker ) )
1052 attacker notify(
"wasp_train_kill" );
1057 if( isdefined(level.hero_power_update))
1059 [[level.hero_power_update]](attacker,
self);
1062 if( RandomIntRange(0,100) >= 80 )
1074 if( isdefined( attacker ) && isai( attacker ) )
1076 attacker notify(
"killed",
self );
1080 self stoploopsound();
1092 self.ignoreall =
false;
1095 self.meleeAttackDist = 64;
1098 self.disableArrivals =
true;
1099 self.disableExits =
true;
1101 if( level.wasp_round_count == 2 )
1105 else if( level.wasp_round_count > 2 )
1115 self waittill(
"death");
1123 self endon(
"death");
1131 players = GetPlayers();
1132 for(i=0;i<players.size;i++)
1134 waspAngle = AngleClamp180( vectorToAngles(
self.origin - players[i].origin )[1] - players[i].angles[1] );
1136 if(isAlive(players[i]) && !isdefined(players[i].revivetrigger))
1138 if ((abs(waspAngle) > 90) && distance2d(
self.origin,players[i].origin) > 100)
1169 function special_wasp_spawn( n_to_spawn = 1, spawn_point, n_radius = 32, n_half_height = 32, b_non_round, spawn_fx =
true, b_return_ai =
false, spawner_override = undefined )
1171 wasp = GetEntArray(
"zombie_wasp",
"targetname" );
1173 if ( isdefined( wasp ) && wasp.size >= 9 )
1179 while ( count < n_to_spawn )
1182 players = GetPlayers();
1184 spawn_enemy = favorite_enemy;
1185 if( !IsDefined( spawn_enemy ) )
1187 spawn_enemy = players[0];
1191 if ( isdefined( level.wasp_spawn_func ) )
1193 spawn_point = [[level.wasp_spawn_func]]( spawn_enemy );
1197 while ( !isdefined( spawn_point ) )
1199 if ( !isdefined( spawn_point ) )
1204 if ( isdefined( spawn_point ) )
1212 spawner = level.wasp_spawners[0];
1214 if( isDefined( spawner_override))
1216 spawner = spawner_override;
1221 v_spawn_origin = spawn_point.origin;
1223 if ( isdefined( ai ) )
1226 queryResult = PositionQuery_Source_Navigation( v_spawn_origin, 0, n_radius, n_half_height, 15,
"navvolume_small" );
1227 if( queryResult.data.size )
1229 point = queryResult.data[ randomint( queryResult.data.size ) ];
1230 v_spawn_origin = point.origin;
1234 ai.does_not_count_to_round = b_non_round;
1239 wait level.zombie_vars[
"zombie_spawn_delay" ];
1252 self endon(
"death" );
1257 self waittill(
"visible" );
1260 if (
self.health > level.wasp_health )
1262 self.maxhealth = level.wasp_health;
1263 self.health = level.wasp_health;
1278 #define MELEE_RANGE_SQ (72 * 72)
1279 #define MELEE_RANGE_Z (64)
1280 #define MELEE_VIEW_DOT 0.5
1284 self endon(
"death" );
1285 self waittill(
"visible" );
1287 while( IsDefined(
self) )
1289 level waittill(
"player_melee", player, weapon );
1291 peye = player GetEye();
1292 dist2 = Distance2DSquared( peye,
self.origin );
1299 pfwd = player GetWeaponForwardDir();
1300 tome =
self.origin - peye;
1301 tome = VectorNormalize( tome );
1302 dot = VectorDot( pfwd, tome );
1307 if ( IsDefined(weapon) )
1310 self DoDamage(
damage, peye, player, player,
"none",
"MOD_MELEE", 0, weapon );
1316 self endon(
"disconnect" );
1319 self waittill(
"weapon_melee", weapon );
1320 level notify(
"player_melee",
self, weapon );
1326 self endon(
"death" );
1327 self endon(
"wasp_running" );
1328 self endon(
"wasp_combat" );
1333 wait randomfloatrange(3,6);
1339 if( isdefined(optional_player_target) )
1341 array::add( level.a_wasp_priority_targets, optional_player_target );
1343 level.zombie_total++;