1 #using scripts\codescripts\struct;
3 #using scripts\shared\clientfield_shared;
4 #using scripts\shared\gameskill_shared;
5 #using scripts\shared\math_shared;
6 #using scripts\shared\statemachine_shared;
7 #using scripts\shared\system_shared;
8 #using scripts\shared\util_shared;
9 #using scripts\shared\turret_shared;
10 #using scripts\shared\flag_shared;
12 #insert scripts\shared\shared.gsh;
13 #insert scripts\shared\statemachine.gsh;
14 #insert scripts\shared\version.gsh;
16 #insert scripts\shared\ai\utility.gsh;
18 #using scripts\shared\vehicle_shared;
19 #using scripts\shared\vehicle_ai_shared;
20 #using scripts\shared\vehicle_death_shared;
23 #define NUM_DAMAGE_STATES 4
24 #define DAMAGE_STATE_THRESHOLD_PCT_1 0.75
25 #define DAMAGE_STATE_THRESHOLD_PCT_2 0.5
26 #define DAMAGE_STATE_THRESHOLD_PCT_3 0.25
27 #define DAMAGE_STATE_THRESHOLD_PCT_4 0.1
29 #define SCAN_HEIGHT_OFFSET 40
31 #define DEFAULT_WEAK_SPOT_DAMAGE_LIMIT 600
33 #define MELEE_RADIUS 270
34 #define MELEE_INNER_RADIUS_DAMAGE 200
35 #define MELEE_OUTER_RADIUS_DAMAGE 150
37 #define NEAR_POINTS_DIST 1000
38 #define FAR_POINTS_DIST 2500
44 #using_animtree( "generic" );
48 vehicle::add_main_callback(
"croc", &
main );
50 SetDvar(
"phys_buoyancy", 1 );
52 level.difficultySettings[
"crocburst_scale" ][
"easy" ] = 1.15;
53 level.difficultySettings[
"crocburst_scale" ][
"normal" ] = 1;
54 level.difficultySettings[
"crocburst_scale" ][
"hardened" ] = 0.85;
55 level.difficultySettings[
"crocburst_scale" ][
"veteran" ] = 0.7;
57 level.difficultySettings[
"crochealth_boost" ][
"easy" ] = -70;
58 level.difficultySettings[
"crochealth_boost" ][
"normal" ] = 0;
59 level.difficultySettings[
"crochealth_boost" ][
"hardened" ] = 70;
60 level.difficultySettings[
"crochealth_boost" ][
"veteran" ] = 140;
65 self useanimtree( #animtree );
72 return GetEntNavMaterial(
self ) == 2;
84 self ASMRequestSubstate( statename +
"@water" );
88 self ASMRequestSubstate( statename +
"@land" );
94 self EnableAimAssist();
95 self SetNearGoalNotifyDist( 35 );
97 self.current_damage_state = 0;
100 self.highlyawareradius = 150;
104 self.fovcosinebusy = 0;
105 self.maxsightdistsqrd = 10000 * 10000;
112 self.goalradius = 10000;
114 if( !isdefined(
self.goalpos ) )
116 self.goalpos =
self.origin;
131 if ( isdefined(
self.script_startstate ) )
133 if(
self.script_startstate ==
"off" )
157 self playsound(
"veh_croc_power_down" );
158 if( isdefined(
self.sndEnt ) )
160 self.sndEnt stoploopsound( .5 );
168 angles =
self GetTagAngles(
"tag_flash" );
169 target_vec =
self.origin + AnglesToForward( ( 0, angles[1], 0 ) ) * 1000;
170 target_vec = target_vec + ( 0, 0, -500 );
171 self SetTargetOrigin( target_vec );
173 if( !isdefined(
self.
emped ) )
175 self DisableAimAssist();
183 self EnableAimAssist();
187 self.off = undefined;
193 self playsound(
"veh_croc_power_up" );
195 for( i = 0; i < 6; i++ )
203 angles =
self GetTagAngles(
"tag_flash" );
204 target_vec =
self.origin + AnglesToForward( ( 0, angles[1], 0 ) ) * 1000;
206 driver =
self GetSeatOccupant( 0 );
207 if( !isdefined(driver) )
209 self SetTargetOrigin( target_vec );
238 self endon(
"death" );
239 self endon(
"change_state" );
240 self endon(
"weapon_fired" );
242 while( isdefined(
self.enemy ) )
244 start =
self GetTagOrigin(
"tag_flash" );
245 enemy_origin =
self.enemy.origin;
247 if( start[2] < enemy_origin[2] )
249 enemy_origin =
self.enemy GetEye();
252 dir_to_enemy = enemy_origin - start;
253 dir_to_enemy = VectorNormalize( dir_to_enemy );
254 right = VectorCross( dir_to_enemy, (0,0,1) );
255 right = VectorNormalize( right );
257 dist_to_enemy_squared = DistanceSquared( start, enemy_origin ) - (5*5);
259 behind_end = enemy_origin + dir_to_enemy * 200;
264 results = bullettrace( start,
end,
false,
self );
266 if( results[
"fraction"] < 1.0 )
268 dist_squared = DistanceSquared( start, results[
"position"] );
269 dist_beyond_enemy = dist_squared - dist_to_enemy_squared;
270 if( dist_beyond_enemy > 0 )
272 self SetTurretTargetVec(
end );
278 end = behind_end + right * 60;
279 results = bullettrace( start,
end,
false,
self );
281 if( results[
"fraction"] < 1.0 )
283 dist_squared = DistanceSquared( start, results[
"position"] );
284 dist_beyond_enemy = dist_squared - dist_to_enemy_squared;
285 if( dist_beyond_enemy > 0 )
287 self SetTurretTargetVec(
end );
293 end = behind_end + right * -60;
294 results = bullettrace( start,
end,
false,
self );
296 if( results[
"fraction"] < 1.0 )
298 dist_squared = DistanceSquared( start, results[
"position"] );
299 dist_beyond_enemy = dist_squared - dist_to_enemy_squared;
300 if( dist_beyond_enemy > 0 )
302 self SetTurretTargetVec(
end );
308 self SetTurretTargetEnt(
self.enemy );
315 self.goalpos =
self.origin;
317 if ( !isdefined( state ) )
330 while( isdefined(
self.
emped ) )
335 self SetSpeed( 5, 5, 5 );
342 if ( isdefined(
self.enemy ) )
351 self playsound(
"veh_croc_emp" );
356 foreach ( player
in level.players )
358 if( !isdefined(
self.enemy ) || player !=
self.enemy )
367 self playsound(
"veh_croc_emp" );
378 self endon(
"death" );
379 self endon(
"change_state" );
380 self endon(
"near_goal" );
381 self endon(
"reached_end_node" );
385 cantSeeEnemyCount = 0;
391 self.move_now =
true;
392 self notify(
"near_goal" );
395 if( isdefined(
self.current_pathto_pos ) )
397 if( isdefined(
self.enemy ) )
399 if( distance2dSquared(
self.enemy.origin,
self.current_pathto_pos ) < 250 * 250 )
401 self.move_now =
true;
402 self notify(
"near_goal" );
405 if( !
self VehCanSee(
self.enemy ) )
410 if( cantSeeEnemyCount > 5 )
412 self.move_now =
true;
413 self notify(
"near_goal" );
419 if( distance2dSquared(
self.current_pathto_pos,
self.goalpos ) >
self.goalradius *
self.goalradius )
423 self.move_now =
true;
424 self notify(
"near_goal" );
434 self endon(
"death" );
435 self endon(
"change_state" );
446 self endon(
"death" );
447 self endon(
"change_state" );
456 self SetBuoyancyOffset( -swimDepth );
461 self endon(
"death" );
464 floatingOffset = base;
468 floatingOffset = -floatingOffset;
470 wait RandomFloatRange( 5, 8 );
476 self endon(
"death" );
477 self endon(
"change_state" );
482 self.sndEnt =
spawn(
"script_origin",
self.origin );
483 self.sndEnt linkto(
self,
"tag_origin" );
484 self.sndEnt playloopsound(
"veh_croc_movement_loop", 2 );
507 goalpos =
self.patrol_node.origin;
511 if(
self SetVehGoalPos( goalpos,
false, 1 ) )
515 self.sndEnt playloopsound(
"veh_croc_movement_loop", 2 );
517 self.current_pathto_pos = goalpos;
523 self ClearVehGoalPos();
527 if( isdefined(
self.move_now ) )
529 self.move_now = undefined;
534 self.sndEnt stoploopsound( .5 );
536 if( !isdefined(
self.getreadytofire ) )
548 self.current_pathto_pos = undefined;
549 self LaunchVehicle( (0,0,-50) );
554 if( isdefined(
self.getreadytofire ) )
556 self.sndEnt stoploopsound( .5 );
558 while( isdefined(
self.getreadytofire ) )
570 sightCheckOrigin = position + (0,0,80);
571 return sighttracepassed( sightCheckOrigin, enemy.origin + (0,0,30),
false,
self );
576 self endon(
"change_state" );
577 self endon(
"death" );
579 if ( !IsDefined(
self.patrol_node ) )
581 /# println(
"^1WARNING: No patrol path defined, taking the nearest one" ); #/
584 while ( !isdefined(
self.patrol_node ) )
586 searchRadius = searchRadius * 2;
587 nodes = GetNodesInRadius(
self.origin, searchRadius, 0, searchRadius,
"Path", 1 );
589 if ( nodes.size > 0 )
591 self.patrol_node = nodes[ 0 ];
597 if ( !isdefined(
self.patrol_start_node ) )
599 self.patrol_start_node =
self.patrol_node;
602 if ( IsDefined(
self.patrol_node.target ) )
604 self.patrol_node = GetNode(
self.patrol_node.target,
"targetname" );
606 if (
self.patrol_node ==
self.patrol_start_node )
608 self notify(
"patrol_route_complete");
618 sweet_spot_dist = 700;
620 quad_tank_radius = 120;
621 height_check_dist = 300;
628 points = arraycombine( closepoints, farpoints,
false,
false );
630 if( points.size < 5 )
636 origin =
self.goalpos;
638 best_point = undefined;
639 best_score = -999999;
641 if ( isdefined(
self.enemy ) )
645 foreach( point
in points )
647 if( distanceSquared( point,
self.goalpos ) >
self.goalRadius *
self.goalRadius )
654 dist_in_front_of_enemy = VectorDot( vec_enemy_to_point, vec_enemy_to_self );
655 dist_away_from_sweet_line = Abs( dist_in_front_of_enemy - sweet_spot_dist );
657 score = 10 + RandomFloat( 1.5 );
659 if( dist_away_from_sweet_line > 160 )
661 score -=
math::clamp( dist_away_from_sweet_line / 1500, 0, 10 );
664 too_far_dist = sweet_spot_dist + 200;
665 dist_from_enemy = distance2dSquared( point,
self.enemy.origin );
666 if( dist_from_enemy > too_far_dist * too_far_dist )
668 score -=
math::clamp( dist_from_enemy / (too_far_dist * too_far_dist), 1, 10 );
671 if( distance2dSquared(
self.origin, point ) < 170 * 170 )
687 if ( score > best_score )
696 foreach( point
in points )
698 score = RandomFloat( 1 );
700 if( distance2dSquared(
self.origin, point ) < 250 )
705 if( score > best_score )
713 if( isdefined( best_point ) )
727 self waittill(
"exit_vehicle", player );
729 player.ignoreme =
false;
730 player DisableInvulnerability();
732 self.goalpos =
self.origin;
737 self endon(
"change_state" );
739 driver =
self GetSeatOccupant( 0 );
740 if( isdefined(driver) )
742 self.turretRotScale = 1;
743 self DisableAimAssist();
746 driver EnableInvulnerability();
747 driver.ignoreme =
true;
755 self ClearTargetEntity();
757 self ClearVehGoalPos();
762 next_damage_state = 0;
764 max_health =
self.healthdefault;
765 if( isdefined(
self.health_max ) )
767 max_health =
self.health_max;
770 health_pct =
self.health / max_health;
774 next_damage_state = 1;
778 next_damage_state = 2;
782 next_damage_state = 3;
786 next_damage_state = 4;
789 if ( next_damage_state !=
self.current_damage_state )
803 self.current_damage_state = next_damage_state;
809 if ( isdefined(
self.damage_fx_ent ) )
810 self.damage_fx_ent Delete();
812 self.damage_fx_ent =
Spawn(
"script_model", ( 0, 0, 0 ) );
813 self.damage_fx_ent SetModel(
"tag_origin" );
814 self.damage_fx_ent.origin =
self.origin;
815 self.damage_fx_ent.angles =
self.angles;
816 self.damage_fx_ent LinkTo(
self,
"tag_turret", (0,0,0), (0,0,0) );
818 return self.damage_fx_ent;
823 if( isdefined(
self.damage_fx_ent ) )
825 self.damage_fx_ent
delete();
828 if( isdefined(
self.stun_fx ) )
830 self.stun_fx
delete();
840 if ( isdefined(
self ) )
842 self playsound(
"veh_croc_power_down" );
847 if( isdefined(
self.sndEnt ) )
849 self.sndEnt stoploopsound( .5 );
859 if ( !isdefined(
self ) )
866 self DisableAimAssist();
884 if ( isdefined(
self ) )
894 if ( isdefined(
self ) )
904 self playsound(
"veh_croc_sparks");
909 self endon(
"crash_done" );
910 self endon(
"death" );
913 self ClearVehGoalPos();
917 self ClearTurretTarget();
926 self notify(
"crash_done" );
932 if( isdefined(
self.
emped ) )
938 self endon(
"death" );
939 self notify(
"emped" );
940 self endon(
"emped" );
943 PlaySoundAtPosition(
"veh_crocemp_down",
self.origin );
944 self.turretRotScale = 0.2;
946 if( !isdefined(
self.stun_fx) )
948 self.stun_fx =
Spawn(
"script_model",
self.origin );
949 self.stun_fx SetModel(
"tag_origin" );
950 self.stun_fx LinkTo(
self,
"tag_turret", (0,0,0), (0,0,0) );
954 wait RandomFloatRange( 4, 8 );
956 self.stun_fx
delete();
958 self.emped = undefined;
961 self playsound (
"veh_boot_croc");
964 function crocCallback_VehicleDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, psOffsetTime, damageFromUnderneath, modelIndex, partName )
966 is_damaged_by_grenade = weapon.weapClass ==
"grenade";
968 if( isdefined( eAttacker ) && ( eAttacker ==
self || eAttacker.team ==
self.team ) )
973 if( partName ==
"tag_target_turret_right" || partName ==
"tag_target_turret_left" ||
974 partName ==
"tag_target_left" || partName ==
"tag_target_right" || partName ==
"tag_target_left1" || partName ==
"tag_target_right1" )
976 self.weak_spot_health -= iDamage;
977 if(
self.weak_spot_health <= 0 )
982 iDamage = Int( iDamage * 3 );
984 self playsound(
"veh_croc_panel_hit" );
987 else if( partName ==
"tag_gunner_barrel1" || partName ==
"tag_gunner_turret1" )
991 self.left_turret_health -= iDamage;
992 if(
self.left_turret_health <= 0 )
997 else if( partName ==
"tag_gunner_barrel2" || partName ==
"tag_gunner_turret2" )
1001 self.right_turret_health -= iDamage;
1002 if(
self.right_turret_health <= 0 )
1007 else if ( is_damaged_by_grenade || sMeansOfDeath ==
"MOD_EXPLOSIVE" )
1009 iDamage = Int( iDamage * 3 );
1014 self.turretRotScale = 1.0;
1018 driver =
self GetSeatOccupant( 0 );
1020 if( weapon.isEmp && sMeansOfDeath !=
"MOD_IMPACT" )
1022 if( !isdefined(driver) )
1028 if( isdefined( driver ) )
1042 if( !isdefined(
self.off ) )
1050 self endon(
"death" );
1063 self endon(
"death" );
1064 self endon(
"recoil_thread" );
1068 self waittill(
"turret_fire" );
1069 angles =
self GetTagAngles(
"tag_barrel" );
1070 dir = AnglesToForward( angles );
1071 self LaunchVehicle( dir * -5,
self.origin + (0,0,30),
false );
1072 Earthquake( 0.2, 0.2, player.origin, 200 );
1078 self notify(
"recoil_thread" );
1079 self endon(
"recoil_thread" );
1080 self endon(
"death" );
1084 player waittill(
"missile_fire" );
1086 angles =
self GetTagAngles(
"tag_barrel" );
1087 dir = AnglesToForward( angles );
1089 self LaunchVehicle( dir * -30,
self.origin + (0,0,70),
false );
1090 Earthquake( 0.4, 0.3, player.origin, 200 );