1 #using scripts\codescripts\struct;
3 #using scripts\shared\ai_shared;
4 #using scripts\shared\array_shared;
5 #using scripts\shared\callbacks_shared;
6 #using scripts\shared\clientfield_shared;
7 #using scripts\shared\fx_shared;
8 #using scripts\shared\laststand_shared;
9 #using scripts\shared\lui_shared;
10 #using scripts\shared\math_shared;
11 #using scripts\shared\scene_shared;
12 #using scripts\shared\spawner_shared;
13 #using scripts\shared\system_shared;
14 #using scripts\shared\util_shared;
15 #using scripts\shared\visionset_mgr_shared;
17 #using scripts\shared\ai\systems\ai_interface;
18 #using scripts\shared\ai\systems\animation_state_machine_utility;
19 #using scripts\shared\ai\systems\animation_state_machine_notetracks;
20 #using scripts\shared\ai\systems\animation_state_machine_mocomp;
21 #using scripts\shared\ai\archetype_utility;
22 #using scripts\shared\ai\systems\behavior_tree_utility;
23 #using scripts\shared\ai\systems\blackboard;
24 #using scripts\shared\ai\systems\debug;
25 #using scripts\shared\ai\systems\gib;
26 #using scripts\shared\ai\zombie_utility;
27 #using scripts\shared\ai\archetype_locomotion_utility;
28 #using scripts\shared\ai\archetype_mocomps_utility;
29 #using scripts\shared\ai\archetype_thrasher_interface;
31 #using scripts\shared\weapons\_weaponobjects;
33 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
34 #insert scripts\shared\ai\systems\animation_state_machine.gsh;
35 #insert scripts\shared\ai\systems\behavior.gsh;
36 #insert scripts\shared\ai\systems\behavior_tree.gsh;
37 #insert scripts\shared\ai\systems\blackboard.gsh;
38 #insert scripts\shared\ai\systems\gib.gsh;
39 #insert scripts\shared\ai\archetype_thrasher.gsh;
40 #insert scripts\shared\ai\utility.gsh;
41 #insert scripts\shared\shared.gsh;
42 #insert scripts\shared\version.gsh;
44 #define N_THRASHER_CHARGE_HEALTH_THRESHOLD_PCT 0.6 //how low health has to be before Thrasher can start charging
46 #namespace ThrasherBehavior;
137 if( entity.thrasherHasTurnedBerserk ===
true )
155 entity.__blackboard = undefined;
166 entity.maxHealth = entity.health;
167 entity.thrasherConsumedPlayer =
false;
170 entity.thrasherIsBerserk =
false;
171 entity.thrasherHasTurnedBerserk =
false;
176 entity.thrasherLastConsume = GetTime();
178 entity.thrasherConsumeCount = 0;
182 entity.thrasherLastTeleportTime = GetTime();
188 entity.thrasherRageCount = 0;
189 entity.thrasherRageLevel = 1;
206 if ( IsDefined( entity.should_turn ) && entity.should_turn )
222 entity.thrasherSpores = [];
226 sporeStruct = SpawnStruct();
228 sporeStruct.dist = thrasherSporeDamageDists[ index ];
230 sporeStruct.maxhealth = sporeStruct.health;
232 sporeStruct.tag = thrasherSpores[ index ];
233 sporeStruct.clientfield = thrasherClientfields[ index ];
235 entity.thrasherSpores[ index ] = sporeStruct;
244 if( isDefined( entity.thrasher_melee_knockdown_function ))
246 entity thread [[ entity.thrasher_melee_knockdown_function ]]();
249 hitEntity = entity Melee();
251 if ( IsDefined( hitEntity ) && IsDefined( entity.thrasherMeleeHitCallback ) )
253 entity thread [[ entity.thrasherMeleeHitCallback ]]( hitEntity );
258 if ( IsDefined( level.attackableCallback ) )
260 entity.attackable [[ level.attackableCallback ]]( entity );
270 if ( entity.thrasherConsumedPlayer )
277 targets = GetPlayers();
280 if ( targets.size == 1 )
285 laststandTargets = [];
287 foreach ( target
in targets )
289 if ( !IsDefined( target.laststandStartTime ) ||
296 if ( IsDefined( target.thrasherFreedTime ) &&
304 !
IS_TRUE( target.thrasherConsumed ) &&
305 DistanceSquared( target.origin, entity.origin ) <= maxConsumeDistanceSq )
307 laststandTargets[ laststandTargets.size ] = target;
311 if ( laststandTargets.size > 0 )
313 sortedPotentialTargets = ArraySortClosest( laststandTargets, entity.origin );
315 return sortedPotentialTargets[0];
338 if ( IsDefined( entity.thrasherMoveModeFriendlyCallback ) )
340 entity [[ entity.thrasherMoveModeFriendlyCallback ]]();
348 if ( IsDefined( laststandPlayer ) )
350 entity.favoriteenemy = laststandPlayer;
351 entity SetGoal( entity.favoriteenemy.origin );
356 entity.ignore_player = [];
357 players = GetPlayers();
359 foreach ( player
in players )
361 if ( player IsNoTarget() ||
364 IS_TRUE( player.thrasherConsumed ) )
366 entity.ignore_player[ entity.ignore_player.size ] = player;
372 if ( IsDefined( entity.thrasherClosestValidPlayer ) )
374 player = [[ entity.thrasherClosestValidPlayer ]]( entity.origin, entity.ignore_player );
381 entity.favoriteenemy = player;
383 if( !IsDefined( player ) || player IsNoTarget() )
385 if( IsDefined( entity.ignore_player ) )
387 if( IsDefined(level._should_skip_ignore_player_logic) && [[level._should_skip_ignore_player_logic]]() )
391 entity.ignore_player = [];
394 entity SetGoal( entity.origin );
397 else if ( IsDefined( entity.attackable ) )
399 if ( IsDefined( entity.attackable_slot ) )
401 entity SetGoal( entity.attackable_slot.origin,
true );
407 if ( IsDefined( targetPos ) )
409 entity SetGoal( targetPos );
414 entity SetGoal( entity.origin );
422 if ( IsDefined( entity.thrasherAttackableObjectCallback ) )
424 return [[ entity.thrasherAttackableObjectCallback ]]( entity );
432 velocity = entity GetVelocity();
434 predicted_pos = entity.origin + ( velocity * predict_time );
435 move_dist_sq = DistanceSquared( predicted_pos, entity.origin );
436 speed = move_dist_sq / predict_time;
444 if( a_filtered_zombies.size > 0 )
446 foreach( zombie
in a_filtered_zombies )
456 if( zombie.knockdown ===
true )
461 knockdown_dist_sq = 48*48;
463 dist_sq = DistanceSquared( predicted_pos, zombie.origin );
465 if( dist_sq > knockdown_dist_sq )
470 origin = thrasher.origin;
472 facing_vec = AnglesToForward( thrasher.angles );
473 enemy_vec = zombie.origin - origin;
475 enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0);
476 facing_yaw_vec = (facing_vec[0], facing_vec[1], 0);
478 enemy_yaw_vec = VectorNormalize( enemy_yaw_vec );
479 facing_yaw_vec = VectorNormalize( facing_yaw_vec );
481 enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec );
497 if( !IsDefined( entity.favoriteenemy ) )
507 if ( entity.favoriteenemy IsNoTarget() )
512 yaw = abs( zombie_utility::getYawToEnemy() );
533 return entity.thrasherIsBerserk && !entity.thrasherHasTurnedBerserk;
538 if ( !IsDefined( entity.favoriteenemy ) )
550 if ( IsDefined( entity.thrasherShouldTeleportCallback ) )
552 return ( [[ entity.thrasherShouldTeleportCallback ]]( entity.origin ) &&
553 [[ entity.thrasherShouldTeleportCallback ]]( entity.favoriteenemy.origin ) );
566 if( !IsDefined( entity.favoriteenemy ) )
571 targets = GetPlayers();
574 if ( targets.size == 1 )
589 if (
IS_TRUE( entity.favoriteenemy.thrasherConsumed ) )
594 if ( IsDefined( entity.thrasherCanConsumePlayerCallback ) && !entity [[ entity.thrasherCanConsumePlayerCallback ]]( entity ) )
604 if ( entity.thrasherConsumeCount >= entity.thrasherConsumeMax )
609 if ( ( entity.thrasherLastConsume + entity.thrasherConsumeCooldown ) >= GetTime() )
614 hasPoppedPustule =
false;
618 sporeStruct = entity.thrasherSpores[ index ];
619 if ( sporeStruct.health <= 0 )
621 hasPoppedPustule =
true;
626 if ( hasPoppedPustule )
628 if ( IsDefined( entity.thrasherCanConsumeCallback ) )
630 return [[ entity.thrasherCanConsumeCallback ]]( entity );
639 if ( IsPlayer( entity.favoriteenemy ) )
652 if ( IsDefined( entity.thrasherConsumeZombieCallback ) )
654 if ( [[ entity.thrasherConsumeZombieCallback ]]( entity ) )
656 entity.thrasherConsumeCount++;
657 entity.thrasherLastConsume = GetTime();
667 #namespace ThrasherServerUtils;
671 zombie.knockdown =
true;
673 zombie_to_thrasher = entity.origin - zombie.origin;
674 zombie_to_thrasher_2d = VectorNormalize( ( zombie_to_thrasher[0], zombie_to_thrasher[1], 0 ) );
676 zombie_forward = AnglesToForward( zombie.angles );
677 zombie_forward_2d = VectorNormalize( ( zombie_forward[0], zombie_forward[1], 0 ) );
679 zombie_right = AnglesToRight( zombie.angles );
680 zombie_right_2d = VectorNormalize( ( zombie_right[0], zombie_right[1], 0 ) );
682 dot = VectorDot( zombie_to_thrasher_2d, zombie_forward_2d );
686 zombie.knockdown_direction =
"front";
689 else if ( dot < 0.5 && dot > -0.5 )
691 dot = VectorDot( zombie_to_thrasher_2d, zombie_right_2d );
694 zombie.knockdown_direction =
"right";
707 zombie.knockdown_direction =
"left";
713 zombie.knockdown_direction =
"back";
720 if ( !entity.thrasherIsBerserk )
723 entity.thrasherIsBerserk =
true;
732 entity.thrasherHasTurnedBerserk =
true;
741 function thrasherDamageCallback( inflictor, attacker,
damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex )
749 entity.thrasherHeadHealth -=
damage;
751 if( entity.thrasherHeadHealth <= 0 )
753 if( isdefined( attacker ) )
755 attacker notify(
"destroyed_thrasher_head" );
766 entity.thrasherStunHealth -=
damage;
767 if ( entity.thrasherStunHealth <= 0 )
770 if ( IsDefined( attacker ) )
772 attacker notify(
"player_stunned_thrasher" );
777 damage =
thrasherSporeDamageCallback( inflictor, attacker,
damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex );
783 if ( IsDefined( attacker ) )
785 attacker notify(
"player_enraged_thrasher" );
789 if (
IS_TRUE( entity.b_thrasher_temp_invulnerable ) )
803 entity endon(
"death" );
804 entity notify(
"end_invulnerability" );
806 entity.b_thrasher_temp_invulnerable =
true;
810 entity.b_thrasher_temp_invulnerable =
false;
813 function thrasherSporeDamageCallback( inflictor, attacker,
damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex )
817 assert( IsDefined( entity.thrasherSpores ) );
819 if ( !IsDefined( point ) )
828 sporeStruct = entity.thrasherSpores[ index ];
829 assert( IsDefined( sporeStruct ) );
831 if ( sporeStruct.health < 0 )
837 tagOrigin = entity GetTagOrigin( sporeStruct.tag );
839 if ( IsDefined( tagOrigin ) && DistanceSquared( tagOrigin, point ) <
SQR( sporeStruct.dist ) )
846 if ( sporeStruct.health <= 0 )
848 entity HidePart( sporeStruct.tag );
852 destroyedSpores |= sporeStruct.clientfield;
855 if ( IsDefined( entity.thrasherPustulePopCallback ) )
857 entity thread [[ entity.thrasherPustulePopCallback ]]( tagOrigin, weapon, attacker );
866 RecordSphere( tagOrigin, sporeStruct.dist,
YELLOW,
"Script", entity );
870 if ( sporeStruct.health > 0 )
876 if ( healthySpores == 0 )
887 if ( IsDefined( entity.thrasherTeleportCallback ) )
889 entity thread [[ entity.thrasherTeleportCallback ]]( entity );
897 if ( IsDefined( entity.thrasherStartTraverseCallback ) )
899 entity [[ entity.thrasherStartTraverseCallback ]]( entity );
905 if ( IsDefined( entity.thrasherTerminateTraverseCallback ) )
907 entity [[ entity.thrasherTerminateTraverseCallback ]]( entity );
913 if( !IsDefined( entity.favoriteenemy ))
915 /#println (
"*** Thrasher is trying to teleport, but has no favorite enemy, so he cannot ***" );#/
920 entity.favoriteenemy.origin,
922 THRASHER_TELEPORT_DESTINATION_MAX_RADIUS,
928 thrashers = GetAIArchetypeArray(
"thrasher" );
931 foreach ( point
in points )
935 foreach ( thrasher
in thrashers )
937 if ( DistanceSquared( point, thrasher.origin ) <= overlapSqr )
946 filteredPoints[ filteredPoints.size ] = point;
951 if( isdefined( entity.thrasher_teleport_dest_func ) )
953 filteredPoints = entity [[entity.thrasher_teleport_dest_func]]( filteredPoints );
956 sortedPoints = ArraySortClosest( filteredPoints, entity.origin );
957 teleport_point = sortedPoints[0];
959 if ( IsDefined( teleport_point ) )
961 v_dir = ( entity.favoriteenemy.origin - teleport_point );
962 v_dir = VectorNormalize( v_dir );
963 v_angles = VectorToAngles( v_dir );
965 entity ForceTeleport( teleport_point, v_angles );
968 entity.thrasherLastTeleportTime = GetTime();
973 entity.thrasherStunStartTime = GetTime();
994 tag = tag + index +
"_root";
998 entity HidePart( tag,
"",
true );
1002 entity ShowPart( tag,
"",
true );
1009 entityNumber = player GetEntityNumber();
1010 entityBit = 1 << entityNumber;
1013 hiddenPlayers = currentHidden;
1017 hiddenPlayers = currentHidden | entityBit;
1021 hiddenPlayers = currentHidden & ~entityBit;
1031 sporeStruct = entity.thrasherSpores[ index ];
1033 if ( sporeStruct.health <= 0 )
1035 entity HidePart( sporeStruct.tag );
1039 entity ShowPart( sporeStruct.tag );
1048 sporeStruct = entity.thrasherSpores[ index ];
1050 if ( sporeStruct.health <= 0 )
1052 sporeStruct.health = sporeStruct.maxhealth;
1056 destroyedSpores &= ~sporeStruct.clientfield;
1068 clone =
Spawn(
"script_model", player.origin );
1069 clone.angles = player.angles;
1072 bodyModel = player GetCharacterBodyModel();
1073 if ( IsDefined( bodyModel ) )
1075 clone SetModel( bodyModel );
1078 headModel = player GetCharacterHeadModel();
1079 if ( IsDefined( headModel ) && headModel !=
"tag_origin" )
1081 if ( IsDefined( clone.head ) )
1083 clone Detach( clone.head );
1086 clone Attach( headModel );
1089 helmetModel = player GetCharacterHelmetModel();
1090 if ( IsDefined( helmetModel ) && headModel !=
"tag_origin" )
1092 clone Attach( helmetModel );
1100 player endon(
"death" );
1102 player waittill(
"hide_body" );
1109 if ( IsDefined( revivee.thrasherConsumed ) && revivee.thrasherConsumed )
1119 thrasher endon(
"consume_scene_end" );
1121 thrasher waittill(
"death" );
1123 if ( IsDefined( thrasher ) )
1125 thrasher
scene::stop(
"scene_zm_dlc2_thrasher_eat_player" );
1128 if ( IsDefined( playerClone ) )
1130 playerClone Delete();
1136 thrasher endon(
"death" );
1139 thrasher
scene::play(
"scene_zm_dlc2_thrasher_eat_player",
array( thrasher, playerClone ) );
1140 thrasher notify(
"consume_scene_end" );
1142 targetPos = GetClosestPointOnNavMesh( thrasher.origin, 1024, 18 );
1144 if ( IsDefined( targetPos ) )
1146 thrasher ForceTeleport( targetPos );
1152 assert( IsActor( thrasher ) );
1153 assert( thrasher.archetype ==
"thrasher" );
1154 assert( IsPlayer( player ) );
1156 thrasher endon(
"kill_consume_player" );
1158 if (
IS_TRUE( player.thrasherConsumed ) )
1164 playerClone.origin = player.origin;
1165 playerClone.angles = player.angles;
1168 thrasher.offsetModel =
Spawn(
"script_model", thrasher.origin );
1172 if ( !IsDefined( thrasher ) ||
IS_TRUE( player.thrasherConsumed ) )
1174 playerClone Destroy();
1180 if ( IsDefined( thrasher.thrasherConsumedCallback ) )
1182 [[ thrasher.thrasherConsumedCallback ]]( thrasher, player );
1185 if ( IsDefined( player.revivetrigger ) )
1187 player.revivetrigger SetInvisibleToAll();
1188 player.revivetrigger TriggerEnable(
false );
1191 player SetClientUIVisibilityFlag(
"hud_visible", 0 );
1192 player SetClientUIVisibilityFlag(
"weapon_hud_visible", 0 );
1193 player.thrasherConsumed =
true;
1194 player.thrasher = thrasher;
1195 player SetPlayerCollision(
false );
1196 player WalkUnderwater(
true );
1197 player.ignoreme =
true;
1198 player HideViewModel();
1199 player FreezeControls(
false );
1200 player FreezeControlsAllowLook(
true );
1206 eyePosition = player GetTagOrigin(
"tag_eye" );
1207 eyeOffset = abs( eyePosition[2] - player.origin[2] ) + 10;
1209 thrasher.offsetModel LinkTo( thrasher,
"tag_camera_thrasher", ( 0, 0, -eyeOffset + 27 ) );
1213 player PlayerLinkTo( thrasher.offsetModel, undefined, 1, 0, 0, 0, 0,
true );
1217 thrasher.thrasherConsumedPlayer =
true;
1218 thrasher.thrasherPlayer = player;
1221 thrasher.thrasherLastTeleportTime = GetTime();
1226 if ( IsDefined( playerClone ) )
1232 player notify(
"player_eaten_by_thrasher" );
1235 thrasher waittill(
"death" );
1242 player endon(
"death" );
1243 player endon(
"kill_thrasher_on_auto_revive" );
1245 player waittill(
"bgb_revive" );
1247 if ( IsDefined( player.thrasher ) )
1249 player.thrasher Kill();
1255 if ( !IsAlive( player ) )
1260 if ( IsDefined( thrasher.offsetModel ) )
1262 thrasher.offsetModel Unlink();
1263 thrasher.offsetModel Delete();
1266 if ( IsDefined( player.revivetrigger ) )
1268 player.revivetrigger SetVisibleToAll();
1269 player.revivetrigger TriggerEnable(
true );
1274 if ( IsDefined( thrasher.thrasherReleaseConsumedCallback ) )
1276 [[ thrasher.thrasherReleaseConsumedCallback ]]( thrasher, player );
1279 thrasher.thrasherPlayer = undefined;
1281 player SetClientUIVisibilityFlag(
"hud_visible", 1 );
1282 player SetClientUIVisibilityFlag(
"weapon_hud_visible", 1 );
1283 player.thrasherFreedTime = GetTime();
1284 player SetStance(
"prone" );
1285 player notify(
"kill_thrasher_on_auto_revive" );
1286 player.thrasherConsumed = undefined;
1287 player.thrasher = undefined;
1288 player WalkUnderwater(
false );
1290 player SetPlayerCollision(
true );
1292 player.ignoreme =
false;
1293 player ShowViewModel();
1294 player FreezeControlsAllowLook(
false );
1300 targetPos = GetClosestPointOnNavMesh( player.origin, 1024, 18 );
1302 if ( IsDefined( targetPos ) )
1304 newPosition = player.origin;
1305 groundPosition = bullettrace( targetPos + (0, 0, -128), targetPos + (0, 0, 128),
false, player );
1307 if ( IsDefined( groundPosition[
"position" ] ) )
1309 newPosition = groundPosition[
"position" ];
1313 groundPosition = bullettrace( targetPos + (0, 0, -256), targetPos + (0, 0, 256),
false, player );
1315 if ( IsDefined( groundPosition[
"position" ] ) )
1317 newPosition = groundPosition[
"position" ];
1321 groundPosition = bullettrace( targetPos + (0, 0, -512), targetPos + (0, 0, 512),
false, player );
1323 if ( IsDefined( groundPosition[
"position" ] ) )
1325 newPosition = groundPosition[
"position" ];
1330 if ( newPosition[2] > player.origin[2] )
1332 player.origin = newPosition;
1337 thrasher notify(
"kill_consume_player" );
1343 self endon(
"death" );
1345 self waittill(
"player_revived" );
1347 self notify(
"achievement_ZM_ISLAND_THRASHER_RESCUE" );
1352 thrasher endon(
"kill_consume_player" );
1354 thrasher.thrasherPlayer = undefined;
1356 characterIndex = player.characterindex;
1358 if ( !IsDefined( characterIndex ) )
1363 level waittill(
"bleed_out", characterIndex );
1365 if ( IsDefined( thrasher.thrasherReleaseConsumedCallback ) )
1367 [[ thrasher.thrasherReleaseConsumedCallback ]]( thrasher, player );
1370 if ( IsDefined( thrasher ) && IsDefined( player ) )
1375 if ( IsDefined( player ) )
1377 player ShowViewModel();
1385 if ( value ==
"normal" )
1387 entity.team =
"axis";
1389 else if ( value ==
"friendly" )
1391 entity.team =
"allies";