#using scripts\codescripts\struct; #using scripts\shared\ai_shared; #using scripts\shared\array_shared; #using scripts\shared\callbacks_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\fx_shared; #using scripts\shared\math_shared; #using scripts\shared\scene_shared; #using scripts\shared\spawner_shared; #using scripts\shared\util_shared; #using scripts\shared\visionset_mgr_shared; #using scripts\shared\_burnplayer; #using scripts\shared\ai\systems\animation_state_machine_utility; #using scripts\shared\ai\systems\animation_state_machine_notetracks; #using scripts\shared\ai\systems\animation_state_machine_mocomp; #using scripts\shared\ai\archetype_utility; #using scripts\shared\ai\systems\behavior_tree_utility; #using scripts\shared\ai\systems\blackboard; #using scripts\shared\ai\systems\debug; #using scripts\shared\ai\systems\gib; #using scripts\shared\ai\zombie_utility; #using scripts\shared\ai\archetype_mocomps_utility; #using scripts\shared\weapons\_weaponobjects; #precache( "xmodel", "c_zom_mech_armor_knee_left" ); #precache( "xmodel", "c_zom_mech_armor_knee_right" ); #precache( "xmodel", "c_zom_mech_armor_shoulder_left" ); #precache( "xmodel", "c_zom_mech_armor_shoulder_right" ); #precache( "xmodel", "c_zom_mech_faceplate" ); #precache( "xmodel", "c_zom_mech_powersupply_cap" ); #precache( "xmodel", "c_zom_mech_gun_barrel" ); #namespace MechzBehavior; function autoexec init() { // INIT BEHAVIORS InitMechzBehaviorsAndASM(); // INIT BLACKBOARD spawner::add_archetype_spawn_function( "mechz", &ArchetypeMechzBlackboardInit ); // INIT MECHZ ON SPAWN spawner::add_archetype_spawn_function( "mechz", &MechzServerUtils::mechzSpawnSetup ); clientfield::register( "actor", "mechz_ft", 5000, 1, "int" ); clientfield::register( "actor", "mechz_faceplate_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_powercap_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_claw_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_115_gun_firing", 5000, 1, "int" ); clientfield::register( "actor", "mechz_rknee_armor_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_lknee_armor_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_rshoulder_armor_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_lshoulder_armor_detached", 5000, 1, "int" ); clientfield::register( "actor", "mechz_headlamp_off", 5000, 2, "int" ); clientfield::register( "actor", "mechz_face", 1, 3, "int" ); } function private InitMechzBehaviorsAndASM() { // SERVICES BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzTargetService",&MechzBehavior::mechzTargetService);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzGrenadeService",&MechzBehavior::mechzGrenadeService);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzBerserkKnockdownService",&MechzBehavior::mechzBerserkKnockdownService);; // CONDITIONS BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldMelee",&MechzBehavior::mechzShouldMelee);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShowPain",&MechzBehavior::mechzShouldShowPain);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShootGrenade",&MechzBehavior::mechzShouldShootGrenade);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShootFlame",&MechzBehavior::mechzShouldShootFlame);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldShootFlameSweep",&MechzBehavior::mechzShouldShootFlameSweep);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldTurnBerserk",&MechzBehavior::mechzShouldTurnBerserk);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldStun",&MechzBehavior::mechzShouldStun);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShouldStumble",&MechzBehavior::mechzShouldStumble);; // ACTIONS BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("mechzStunLoop",&MechzBehavior::mechzStunStart,&MechzBehavior::mechzStunUpdate,&MechzBehavior::mechzStunEnd);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("mechzStumbleLoop",&MechzBehavior::mechzStumbleStart,&MechzBehavior::mechzStumbleUpdate,&MechzBehavior::mechzStumbleEnd);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("mechzShootFlameAction",&MechzBehavior::mechzShootFlameActionStart,&MechzBehavior::mechzShootFlameActionUpdate,&MechzBehavior::mechzShootFlameActionEnd);; // FUNCTIONS BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShootGrenade",&MechzBehavior::mechzShootGrenade);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzShootFlame",&MechzBehavior::mechzShootFlame);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzUpdateFlame",&MechzBehavior::mechzUpdateFlame);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzStopFlame",&MechzBehavior::mechzStopFlame);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzPlayedBerserkIntro",&MechzBehavior::mechzPlayedBerserkIntro);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzAttackStart",&MechzBehavior::mechzAttackStart);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzDeathStart",&MechzBehavior::mechzDeathStart);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzIdleStart",&MechzBehavior::mechzIdleStart);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzPainStart",&MechzBehavior::mechzPainStart);; BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("mechzPainTerminate",&MechzBehavior::mechzPainTerminate);; // MOCOMPS // NOTETRACKS AnimationStateNetwork::RegisterNotetrackHandlerFunction("melee_soldat",&MechzBehavior::mechzNotetrackMelee);; AnimationStateNetwork::RegisterNotetrackHandlerFunction("fire_chaingun",&MechzBehavior::mechzNotetrackShootGrenade);; } function private ArchetypeMechzBlackboardInit() { // CREATE BLACKBOARD Blackboard::CreateBlackBoardForEntity( self ); // USE UTILITY BLACKBOARD self AiUtility::RegisterUtilityBlackboardAttributes(); // CREATE MECHZ BLACKBOARD Blackboard::RegisterBlackBoardAttribute(self,"_locomotion_speed","locomotion_speed_run",undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_locomotion_speed");#/ }; Blackboard::RegisterBlackBoardAttribute(self,"_locomotion_should_turn","should_not_turn",&BB_GetShouldTurn); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_locomotion_should_turn");#/ }; Blackboard::RegisterBlackBoardAttribute(self,"_zombie_damageweapon_type","regular",undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_zombie_damageweapon_type");#/ }; Blackboard::RegisterBlackBoardAttribute(self,"_mechz_part","mechz_powercore",undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_mechz_part");#/ }; // REGISTER ANIMSCRIPTED CALLBACK self.___ArchetypeOnAnimscriptedCallback = &ArchetypeMechzOnAnimscriptedCallback; // ENABLE DEBUGGING IN ODYSSEY /#self FinalizeTrackedBlackboardAttributes();#/; } function private ArchetypeMechzOnAnimscriptedCallback( entity ) { // UNREGISTER THE BLACKBOARD entity.__blackboard = undefined; // REREGISTER BLACKBOARD entity ArchetypeMechzBlackboardInit(); } function private BB_GetShouldTurn() { if( IsDefined( self.should_turn ) && self.should_turn ) { return "should_turn"; } return "should_not_turn"; } //---------------------------------------------------------------------------------------------------------------------------- // NOTETRACK HANDLERS //---------------------------------------------------------------------------------------------------------------------------- function private mechzNotetrackMelee( entity ) { if( isDefined( entity.mechz_melee_knockdown_function )) { entity thread [[ entity.mechz_melee_knockdown_function ]](); } entity Melee(); } function private mechzNotetrackShootGrenade( entity ) { if ( !IsDefined( entity.enemy ) ) { return; } // shoot_angle = RandomIntRange( MECHZ_GRENADE_DEVIATION_YAW_MIN, MECHZ_GRENADE_DEVIATION_YAW_MAX ); // up_angle = RandomIntRange( MECHZ_GRENADE_DEVIATION_PITCH_MIN, MECHZ_GRENADE_DEVIATION_PITCH_MAX ); base_target_pos = entity.enemy.origin; v_velocity = entity.enemy GetVelocity(); base_target_pos = base_target_pos + ( v_velocity * 1.5 ); target_pos_offset_x = math::randomsign() * randomint( 32 ); target_pos_offset_y = math::randomsign() * randomint( 32 ); target_pos = base_target_pos + ( target_pos_offset_x, target_pos_offset_y, 0 ); dir = VectorToAngles( target_pos - entity.origin ); // dir = ( dir[0] - up_angle, dir[1] + shoot_angle, dir[2] ); dir = AnglesToForward( dir ); launch_offset = (dir * 5); launch_pos = entity GetTagOrigin( "tag_gun_barrel2" ) + launch_offset; dist = Distance( launch_pos, target_pos ); velocity = dir * dist; velocity = velocity + (0,0,120); val = 1; oldval = entity clientfield::get( "mechz_115_gun_firing" ); if( oldval === val ) { val = 0; } entity clientfield::set( "mechz_115_gun_firing", val ); entity MagicGrenadeType( GetWeapon("electroball_grenade"), launch_pos, velocity ); PlaySoundAtPosition ("wpn_grenade_fire_mechz", entity.origin); } //---------------------------------------------------------------------------------------------------------------------------- // BEHAVIOR TREE //---------------------------------------------------------------------------------------------------------------------------- function mechzTargetService( entity ) { if ( ( isdefined( entity.ignoreall ) && entity.ignoreall ) ) { return false; } if ( IsDefined( entity.destroy_octobomb ) ) { return false; } player = zombie_utility::get_closest_valid_player( self.origin, self.ignore_player ); entity.favoriteenemy = player; if( !IsDefined( player ) || player IsNoTarget() ) { if( IsDefined( entity.ignore_player ) ) { if(isDefined(level._should_skip_ignore_player_logic) && [[level._should_skip_ignore_player_logic]]() ) { return; } entity.ignore_player = []; } /#if ( ( isdefined( level.b_mechz_true_ignore ) && level.b_mechz_true_ignore ) ) { entity SetGoal( entity.origin ); return false; }#/ if( isdefined( level.no_target_override ) ) { [[ level.no_target_override ]]( entity ); } else { entity SetGoal( entity.origin ); } return false; } else { if( isDefined( level.enemy_location_override_func )) { enemy_ground_pos = [[level.enemy_location_override_func]]( entity, player); if( isDefined( enemy_ground_pos )) { entity SetGoal( enemy_ground_pos); return true; } } targetPos = GetClosestPointOnNavMesh( player.origin, 64, 30 ); if ( IsDefined( targetPos ) ) { entity SetGoal( targetPos ); return true; } else { entity SetGoal( entity.origin ); return false; } } } function private mechzGrenadeService( entity ) { if( !isDefined( entity.burstGrenadesFired )) { entity.burstGrenadesFired = 0; } if ( entity.burstGrenadesFired >= 3 ) { if ( GetTime() > entity.nextGrenadeTime ) { entity.burstGrenadesFired = 0; } } if( isDefined( level.a_electroball_grenades )) { level.a_electroball_grenades = array::remove_undefined( level.a_electroball_grenades ); a_active_grenades = array::filter( level.a_electroball_grenades, false, &mechzFilterGrenadesByOwner, entity ); entity.activeGrenades = a_active_grenades.size; } else { entity.activeGrenades = 0; } } function private mechzFilterGrenadesByOwner( grenade, mechz ) { if( grenade.owner === mechz ) { return true; } return false; } function private mechzBerserkKnockdownService( entity ) { velocity = entity GetVelocity(); predict_time = 0.3; predicted_pos = entity.origin + ( velocity * predict_time ); move_dist_sq = DistanceSquared( predicted_pos, entity.origin ); speed = move_dist_sq / predict_time; if( speed >= 10 ) { a_zombies = GetAIArchetypeArray( "zombie" ); a_filtered_zombies = array::filter( a_zombies, false, &mechzZombieEligibleForBerserkKnockdown, entity, predicted_pos ); if( a_filtered_zombies.size > 0 ) { foreach( zombie in a_filtered_zombies ) { zombie.knockdown = true; zombie.knockdown_type = "knockdown_shoved"; zombie_to_mechz = entity.origin - zombie.origin; zombie_to_mechz_2d = VectorNormalize( ( zombie_to_mechz[0], zombie_to_mechz[1], 0 ) ); zombie_forward = AnglesToForward( zombie.angles ); zombie_forward_2d = VectorNormalize( ( zombie_forward[0], zombie_forward[1], 0 ) ); zombie_right = AnglesToRight( zombie.angles ); zombie_right_2d = VectorNormalize( ( zombie_right[0], zombie_right[1], 0 ) ); dot = VectorDot( zombie_to_mechz_2d, zombie_forward_2d ); if( dot >= 0.5 ) { zombie.knockdown_direction = "front"; zombie.getup_direction = "getup_back"; } else if ( dot < 0.5 && dot > -0.5 ) { dot = VectorDot( zombie_to_mechz_2d, zombie_right_2d ); if( dot > 0 ) { zombie.knockdown_direction = "right"; if ( math::cointoss() ) { zombie.getup_direction = "getup_back"; } else { zombie.getup_direction = "getup_belly"; } } else { zombie.knockdown_direction = "left"; zombie.getup_direction = "getup_belly"; } } else { zombie.knockdown_direction = "back"; zombie.getup_direction = "getup_belly"; } } } } } function private mechzZombieEligibleForBerserkKnockdown( zombie, mechz, predicted_pos ) { if( zombie.knockdown === true ) { return false; } knockdown_dist_sq = 48*48; dist_sq = DistanceSquared( predicted_pos, zombie.origin ); if( dist_sq > knockdown_dist_sq ) { return false; } if( zombie.is_immune_to_knockdown === true ) { return false; } origin = mechz.origin; facing_vec = AnglesToForward( mechz.angles ); enemy_vec = zombie.origin - origin; enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0); facing_yaw_vec = (facing_vec[0], facing_vec[1], 0); enemy_yaw_vec = VectorNormalize( enemy_yaw_vec ); facing_yaw_vec = VectorNormalize( facing_yaw_vec ); enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec ); if( enemy_dot < 0 )// is enemy behind mechz { return false; } return true; } //---------------------------------------------------------------------------------------------------------------------------------- // CONDITIONS //---------------------------------------------------------------------------------------------------------------------------------- function mechzShouldMelee( entity ) { if( !IsDefined( entity.enemy ) ) { return false; } if( DistanceSquared( entity.origin, entity.enemy.origin ) > 112 * 112 ) { return false; } // don't do yaw check if on vehicle ( turret ). add any additional checks above this. if( ( isdefined( entity.enemy.usingvehicle ) && entity.enemy.usingvehicle ) ) { return true; } yaw = abs( zombie_utility::getYawToEnemy() ); if( ( yaw > 45 ) ) { return false; } return true; } function private mechzShouldShowPain( entity ) { if( entity.partDestroyed === true ) { return true; } return false; } function private mechzShouldShootGrenade( entity ) { if( entity.berserk === true ) { return false; } if( entity.gun_attached !== true ) { return false; } if ( !IsDefined( entity.favoriteenemy ) ) { return false; } if ( entity.burstGrenadesFired >= 3 ) { return false; } if( entity.activeGrenades >= 9 ) { return false; } if ( !entity MechzServerUtils::mechzGrenadeCheckInArc() ) { return false; } if( !entity CanSee( entity.favoriteenemy ) ) { return false; } dist_sq = DistanceSquared( entity.origin, entity.favoriteenemy.origin ); if ( dist_sq < 250 * 250 || dist_sq > 1200 * 1200 ) { return false; } return true; } function private mechzShouldShootFlame( entity ) { /# if ( ( isdefined( entity.shoot_flame ) && entity.shoot_flame ) ) { return true; } #/ if( entity.berserk === true ) { return false; } if ( ( isdefined( entity.isShootingFlame ) && entity.isShootingFlame ) && GetTime() < entity.stopShootingFlameTime ) { return true; } if ( !IsDefined( entity.favoriteenemy ) ) { return false; } if( entity.isShootingFlame === true && entity.stopShootingFlameTime <= GetTime() ) { return false; } if ( entity.nextFlameTime > GetTime() ) { return false; } if ( !entity MechzServerUtils::mechzCheckInArc( 26, "tag_flamethrower_fx" ) ) { return false; } dist_sq = DistanceSquared( entity.origin, entity.favoriteenemy.origin ); if ( dist_sq < 96 * 96 || dist_sq > 225 * 225 ) { return false; } can_see = BulletTracePassed( entity.origin + ( 0, 0, 36 ), entity.favoriteenemy.origin + ( 0, 0, 36 ), false, undefined ); if ( !can_see ) { return false; } return true; } function private mechzShouldShootFlameSweep( entity ) { if( entity.berserk === true ) { return false; } if ( !mechzShouldShootFlame( entity ) ) { return false; } if ( RandomInt( 100 ) > 10 ) { return false; } near_players = 0; players = GetPlayers(); foreach( player in players ) { if ( Distance2DSquared( entity.origin, player.origin ) < 100 * 100 ) { near_players++; } } if ( near_players < 2 ) { return false; } return true; } function private mechzShouldTurnBerserk( entity ) { if( entity.berserk === true && entity.hasTurnedBerserk !== true ) { return true; } return false; } function private mechzShouldStun( entity ) { if ( ( isdefined( entity.stun ) && entity.stun ) ) { return true; } return false; } function private mechzShouldStumble( entity ) { if ( ( isdefined( entity.stumble ) && entity.stumble ) ) { return true; } return false; } //---------------------------------------------------------------------------------------------------------------------------------- // ACTIONS //---------------------------------------------------------------------------------------------------------------------------------- function private mechzShootGrenadeAction( entity, asmStateName ) { AnimationStateNetworkUtility::RequestState( entity, asmStateName ); entity.grenadeStartTime = GetTime() + 3000; return 5; } function private mechzShootGrenadeActionUpdate( entity, asmStateName ) { if ( !( isdefined( entity.shoot_grenade ) && entity.shoot_grenade ) ) { return 4; } return 5; } function private mechzStunStart( entity, asmStateName ) { AnimationStateNetworkUtility::RequestState( entity, asmStateName ); entity.stunTime = GetTime() + 500; return 5; } function private mechzStunUpdate( entity, asmStateName ) { if ( GetTime() > entity.stunTime ) { return 4; } return 5; } function private mechzStunEnd( entity, asmStateName ) { entity.stun = false; entity.stumble_stun_cooldown_time = GetTime() + 10000; return 4; } function private mechzStumbleStart( entity, asmStateName ) { AnimationStateNetworkUtility::RequestState( entity, asmStateName ); entity.stumbleTime = GetTime() + 500; return 5; } function private mechzStumbleUpdate( entity, asmStateName ) { if ( GetTime() > entity.stumbleTime ) { return 4; } return 5; } function private mechzStumbleEnd( entity, asmStateName ) { entity.stumble = false; entity.stumble_stun_cooldown_time = GetTime() + 10000; return 4; } function mechzShootFlameActionStart( entity, asmStateName ) { AnimationStateNetworkUtility::RequestState( entity, asmStateName ); mechzShootFlame( entity ); return 5; } function mechzShootFlameActionUpdate( entity, asmStateName ) { if( ( isdefined( entity.berserk ) && entity.berserk )) { mechzStopFlame( entity ); return 4; } if( ( isdefined( mechzShouldMelee( entity ) ) && mechzShouldMelee( entity ) ) ) { mechzStopFlame( entity ); return 4; } if ( ( isdefined( entity.isShootingFlame ) && entity.isShootingFlame ) ) { if ( IsDefined( entity.stopShootingFlameTime ) && GetTime() > entity.stopShootingFlameTime ) { mechzStopFlame( entity ); return 4; } mechzUpdateFlame( entity ); } return 5; } function mechzShootFlameActionEnd( entity, asmStateName ) { mechzStopFlame( entity ); return 4; } //---------------------------------------------------------------------------------------------------------------------------------- // FUNCTIONS //---------------------------------------------------------------------------------------------------------------------------------- function private mechzShootGrenade( entity ) { entity.burstGrenadesFired ++; if ( entity.burstGrenadesFired >= 3 ) { entity.nextGrenadeTime = GetTime() + 6000; } } function private mechzShootFlame( entity ) { entity thread mechzDelayFlame(); } function private mechzDelayFlame() { self endon( "death" ); self notify( "mechzDelayFlame" ); self endon( "mechzDelayFlame" ); wait( 0.3 ); self clientfield::set( "mechz_ft", 1 ); self.isShootingFlame = true; self.stopShootingFlameTime = GetTime() + 2500; } function private mechzUpdateFlame( entity ) { if( IsDefined( level.mechz_flamethrower_player_callback ) ) { [[level.mechz_flamethrower_player_callback]]( entity ); } else { players = GetPlayers(); foreach( player in players ) { if ( !( isdefined( player.is_burning ) && player.is_burning ) ) { if ( player IsTouching( entity.flameTrigger ) ) { if ( IsDefined( entity.mechzFlameDamage ) ) { player thread [[ entity.mechzFlameDamage ]](); } else { player thread playerFlameDamage(entity); } } } } } if( IsDefined( level.mechz_flamethrower_ai_callback ) ) { [[level.mechz_flamethrower_ai_callback]](entity); } } function playerFlameDamage(mechz) { self endon( "death" ); self endon( "disconnect" ); if ( !( isdefined( self.is_burning ) && self.is_burning ) && zombie_utility::is_player_valid( self, true ) ) { self.is_burning = 1; if ( !self HasPerk( "specialty_armorvest" ) ) { self burnplayer::setPlayerBurning( 1.5, 0.5, 30, mechz, undefined ); } else { self burnplayer::setPlayerBurning( 1.5, 0.5, 20, mechz, undefined ); } wait( 1.5 ); self.is_burning = 0; } } function mechzStopFlame( entity ) { self notify( "mechzDelayFlame" ); entity clientfield::set( "mechz_ft", 0 ); entity.isShootingFlame = false; entity.nextFlameTime = GetTime() + 7500; entity.stopShootingFlameTime = undefined; } function mechzGoBerserk() { entity = self; g_time = GetTime(); // entity ASMSetAnimationRate( 1.04 ); entity.berserkEndTime = g_time + 10000; if( entity.berserk !== true ) { entity.berserk = true; entity thread mechzEndBerserk(); Blackboard::SetBlackBoardAttribute( entity, "_locomotion_speed", "locomotion_speed_sprint" ); } } function private mechzPlayedBerserkIntro( entity ) { entity.hasTurnedBerserk = true; } function private mechZEndBerserk() { self endon( "death" ); self endon( "disconnect" ); While( self.berserk === true ) { if( GetTime() >= self.berserkEndTime ) { self.berserk = false; self.hasTurnedBerserk = false; self ASMSetAnimationRate( 1.0 ); Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_run" ); } wait 0.25; } } function private mechzAttackStart( entity ) { entity clientfield::set( "mechz_face", 1 ); } function private mechzDeathStart( entity ) { entity clientfield::set( "mechz_face", 2 ); } function private mechzIdleStart( entity ) { entity clientfield::set( "mechz_face", 3 ); } function private mechzPainStart( entity ) { entity clientfield::set( "mechz_face", 4 ); } function private mechzPainTerminate( entity ) { entity.partDestroyed = false; entity.show_pain_from_explosive_dmg = undefined; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MOCOMPS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #namespace MechzServerUtils; function private mechzSpawnSetup() { self DisableAimAssist(); self.disableAmmoDrop = true; self.no_gib = true; self.ignore_nuke = true; self.ignore_enemy_count = true; self.ignore_round_robbin_death = true; self.zombie_move_speed = "run"; Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_run" ); self.ignoreRunAndGunDist = true; self mechzAddAttachments(); self.grenadeCount = 9; self.nextFlameTime = GetTime(); self.stumble_stun_cooldown_time = GetTime(); /# self.debug_traversal_ast = "traverse@mechz"; #/ self.flameTrigger = Spawn( "trigger_box", self.origin, 0, 200, 50, 25 ); self.flameTrigger EnableLinkTo(); self.flameTrigger.origin = self GetTagOrigin( "tag_flamethrower_fx" ); self.flameTrigger.angles = self GetTagAngles( "tag_flamethrower_fx" ); self.flameTrigger LinkTo( self, "tag_flamethrower_fx" ); self thread weaponobjects::watchWeaponObjectUsage(); // necessary array creation element for weapon watcher stuff used in the electroball grenade self.pers = []; self.pers["team"] = self.team; //self thread mechzFlameWatcher(); } function private mechzFlameWatcher() { self endon( "death" ); while ( 1 ) { if ( IsDefined( self.favoriteenemy ) ) { if ( self.flameTrigger IsTouching( self.favoriteenemy ) ) { /# PrintTopRightLn( "flame on" ); #/ } } {wait(.05);}; } } function private mechzAddAttachments() { self.has_left_knee_armor = true; self.left_knee_armor_health = 50; self.has_right_knee_armor = true; self.right_knee_armor_health = 50; self.has_left_shoulder_armor = true; self.left_shoulder_armor_health = 50; self.has_right_shoulder_armor = true; self.right_shoulder_armor_health = 50; org = self GetTagOrigin( "tag_gun_spin" ); ang = self GetTagAngles( "tag_gun_spin" ); self.gun_attached = true; self.has_faceplate = true; self.faceplate_health = 50; self.has_powercap = true; self.powercap_covered = true; self.powercap_cover_health = 50; self.powercap_health = 50; } function mechzDamageCallback( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex ) { if( isDefined( self.b_flyin_done ) && !( isdefined( self.b_flyin_done ) && self.b_flyin_done ) ) { return 0; } if ( isDefined( level.mechz_should_stun_override ) && !( ( isdefined( self.stun ) && self.stun ) || ( isdefined( self.stumble ) && self.stumble ) ) ) { if ( self.stumble_stun_cooldown_time < GetTime() && !( isdefined( self.berserk ) && self.berserk ) ) { self [[level.mechz_should_stun_override]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex ); } } if ( IsSubStr( weapon.name, "elemental_bow" ) && isdefined( inflictor ) && inflictor.classname === "rocket" ) { // AkiA: Damage from bow shots that hit Mechz directly is controlled in zm_weap_elemental_bow::mechz_direct_hit_impact_damage_check() return 0; } damage = mechzWeaponDamageModifier( damage, weapon ); if( isdefined( level.mechz_damage_override ) ) { damage = [[level.mechz_damage_override]]( attacker, damage ); } // play audio pain if he hasn't been hit in a bit if( !isDefined( self.next_pain_time ) || GetTime() >= self.next_pain_time ) { self thread mechz_play_pain_audio(); self.next_pain_time = GetTime() + 250 + RandomInt( 500 ); //will wait this long before playing a pain audio again } if( isDefined( self.damage_scoring_function )) { self [[ self.damage_scoring_function ]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex ); } if( IsDefined( level.mechz_staff_damage_override ) ) { staffDamage = [[ level.mechz_staff_damage_override ]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex ); if( staffDamage > 0 ) { n_mechz_damage_percent = 0.5; // Boost damage if his helmet is off and the weapon is a staff if( !( isdefined( self.has_faceplate ) && self.has_faceplate ) && n_mechz_damage_percent < 1.0 ) { n_mechz_damage_percent = 1.0; } staffDamage = staffDamage * n_mechz_damage_percent; if( ( isdefined( self.has_faceplate ) && self.has_faceplate ) ) { self mechz_track_faceplate_damage( staffDamage ); } /#iPrintLnBold( "Staff DMG: " + staffDamage + ". HP: " + ( self.health - staffDamage ) );#/ if(!isdefined(self.explosive_dmg_taken))self.explosive_dmg_taken=0; self.explosive_dmg_taken += staffDamage; if( IsDefined( level.mechz_explosive_damage_reaction_callback ) ) { self [[ level.mechz_explosive_damage_reaction_callback ]](); } return staffDamage; } } if( IsDefined( level.mechz_explosive_damage_reaction_callback ) ) { if( isDefined( mod ) && mod == "MOD_GRENADE" || mod == "MOD_GRENADE_SPLASH" || mod == "MOD_PROJECTILE" || mod == "MOD_PROJECTILE_SPLASH" || mod == "MOD_EXPLOSIVE" ) { n_mechz_damage_percentage = 0.5; if( isDefined( attacker ) && IsPlayer( attacker ) && IsAlive( attacker ) && ( level.zombie_vars[attacker.team]["zombie_insta_kill"] || ( isdefined( attacker.personal_instakill ) && attacker.personal_instakill )) ) //instakill does normal damage { n_mechz_damage_percentage = 1.0; } explosive_damage = damage * n_mechz_damage_percentage; if(!isdefined(self.explosive_dmg_taken))self.explosive_dmg_taken=0; self.explosive_dmg_taken += explosive_damage; if( ( isdefined( self.has_faceplate ) && self.has_faceplate ) ) { self mechz_track_faceplate_damage( explosive_damage ); } self [[ level.mechz_explosive_damage_reaction_callback ]](); /#iPrintLnBold( "Explosive DMG: " + explosive_damage + ". HP: " + ( self.health - explosive_damage ) );#/ return explosive_damage; } } if ( hitLoc == "head" ) { attacker show_hit_marker(); /#iPrintLnBold( "Head DMG: " + damage + ". HP: " + ( self.health - damage ) );#/ return damage; } if( hitloc !== "none" ) { switch( hitLoc ) { case "torso_upper": if( self.has_faceplate == true ) { faceplate_pos = self GetTagOrigin( "j_faceplate" ); dist_sq = DistanceSquared( faceplate_pos, point ); if( dist_sq <= 144 ) { self mechz_track_faceplate_damage( damage ); attacker show_hit_marker(); } headlamp_dist_sq = DistanceSquared( point, self GetTagOrigin( "tag_headlamp_FX" )); if( headlamp_dist_sq <= 9 ) { self MechzServerUtils::mechz_turn_off_headlamp( true ); } } partName = GetPartName( "c_zom_mech_body", boneIndex ); if( self.powercap_covered === true && ( partName === "tag_powersupply" || partName === "tag_powersupply_hit" ) ) { self mechz_track_powercap_cover_damage( damage ); attacker show_hit_marker(); /#iPrintLnBold( "PowerCore/Supply DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; } else if( self.powercap_covered !== true && self.has_powercap === true && ( partName === "tag_powersupply" || partName === "tag_powersupply_hit" ) ) { self mechz_track_powercap_damage( damage ); attacker show_hit_marker(); /#iPrintLnBold( "PowerCore/Supply DMG: " + damage + ". HP: " + ( self.health - damage ) );#/ return damage; } else if( self.powercap_covered !== true && self.has_powercap !== true && ( partName === "tag_powersupply" || partName === "tag_powersupply_hit" ) ) { /#iPrintLnBold( "PowerCore/Supply DMG: " + ( damage * 0.5 ) + ". HP: " + ( self.health - ( damage * 0.5 ) ) );#/ attacker show_hit_marker(); return damage * 0.5; } if( self.has_right_shoulder_armor === true && partName === "j_shoulderarmor_ri" ) { self mechz_track_rshoulder_armor_damage( damage ); /#iPrintLnBold( "Torso Upper DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; } if( self.has_left_shoulder_armor === true && partName === "j_shoulderarmor_le" ) { self mechz_track_lshoulder_armor_damage( damage ); /#iPrintLnBold( "Torso Upper DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; } /#iPrintLnBold( "Torso Upper DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; break; case "left_leg_lower": partName = GetPartName( "c_zom_mech_body", boneIndex ); if( partName === "j_knee_attach_le" && self.has_left_knee_armor === true ) { self mechz_track_lknee_armor_damage( damage ); } /#iPrintLnBold( "HitLoc L Leg Lower DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; break; case "right_leg_lower": partName = GetPartName( "c_zom_mech_body", boneIndex ); if( partName === "j_knee_attach_ri" && self.has_right_knee_armor === true ) { self mechz_track_rknee_armor_damage( damage ); } /#iPrintLnBold( "HitLoc R Leg Lower DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; break; case "left_hand": case "left_arm_lower": case "left_arm_upper": if ( IsDefined( level.mechz_left_arm_damage_callback ) ) { self [[ level.mechz_left_arm_damage_callback ]](); } /#iPrintLnBold( "HitLoc L Arm DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; break; default: /#iPrintLnBold( "HitLoc DEFAULT DMG: " + ( damage * 0.1 ) + ". HP: " + ( self.health - ( damage * 0.1 ) ) );#/ return damage * 0.1; break; } } if ( mod == "MOD_PROJECTILE" ) { hit_damage = damage * 0.1; if( self.has_faceplate !== true ) { head_pos = self GetTagOrigin( "tag_eye" ); dist_sq = DistanceSquared( head_pos, point ); if( dist_sq <= 144 ) { /#iPrintLnBold( "Projectile head DMG: " + damage + ". HP: " + ( self.health - damage ) );#/ attacker show_hit_marker(); return damage; } } if( self.has_faceplate === true ) { faceplate_pos = self GetTagOrigin( "j_faceplate" ); dist_sq = DistanceSquared( faceplate_pos, point ); if( dist_sq <= 144 ) { self mechz_track_faceplate_damage( damage ); attacker show_hit_marker(); } headlamp_dist_sq = DistanceSquared( point, self GetTagOrigin( "tag_headlamp_FX" )); if( headlamp_dist_sq <= 9 ) { self MechzServerUtils::mechz_turn_off_headlamp( true ); } } power_pos = self GetTagOrigin( "tag_powersupply_hit" ); power_dist_sq = DistanceSquared( power_pos, point ); if( power_dist_sq <= 25 ) { if( self.powercap_covered !== true && self.has_powercap !== true ) { /#iPrintLnBold( "Projectile powercap DMG: " + damage + ". HP: " + ( self.health - damage ) );#/ attacker show_hit_marker(); return damage; } if( self.powercap_covered !== true && self.has_powercap === true ) { self mechz_track_powercap_damage( damage ); attacker show_hit_marker(); /#iPrintLnBold( "Projectile powercap DMG: " + damage + ". HP: " + ( self.health - damage ) );#/ return damage; } if( self.powercap_covered === true ) { self mechz_track_powercap_cover_damage( damage ); attacker show_hit_marker(); } } if( self.has_right_shoulder_armor === true ) { armor_pos = self GetTagOrigin( "j_shoulderarmor_ri" ); dist_sq = DistanceSquared( armor_pos, point ); if( dist_sq <= 64 ) { self mechz_track_rshoulder_armor_damage( damage ); } } if( self.has_left_shoulder_armor === true ) { armor_pos = self GetTagOrigin( "j_shoulderarmor_le" ); dist_sq = DistanceSquared( armor_pos, point ); if( dist_sq <= 64 ) { self mechz_track_lshoulder_armor_damage( damage ); } } if( self.has_right_knee_armor === true ) { armor_pos = self GetTagOrigin( "j_knee_attach_ri" ); dist_sq = DistanceSquared( armor_pos, point ); if( dist_sq <= 36 ) { self mechz_track_rknee_armor_damage( damage ); } } if( self.has_left_knee_armor === true ) { armor_pos = self GetTagOrigin( "j_knee_attach_le" ); dist_sq = DistanceSquared( armor_pos, point ); if( dist_sq <= 36 ) { self mechz_track_lknee_armor_damage( damage ); } } /#iPrintLnBold( "Projectile DMG: " + hit_damage + ". HP: " + ( self.health - hit_damage ) );#/ return hit_damage; } else if ( mod == "MOD_PROJECTILE_SPLASH" ) { hit_damage = damage * 0.2; //count number of armor pieces i_num_armor_pieces = 0; if( isDefined( level.mechz_faceplate_damage_override )) { self [[level.mechz_faceplate_damage_override]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex ); } if( self.has_right_shoulder_armor === true ) { i_num_armor_pieces += 1; right_shoulder_index = i_num_armor_pieces; } if( self.has_left_shoulder_armor === true ) { i_num_armor_pieces += 1; left_shoulder_index = i_num_armor_pieces; } if( self.has_right_knee_armor === true ) { i_num_armor_pieces += 1; right_knee_index = i_num_armor_pieces; } if( self.has_left_knee_armor === true ) { i_num_armor_pieces += 1; left_knee_index = i_num_armor_pieces; } if( i_num_armor_pieces > 0 ) { if( i_num_armor_pieces <= 1 ) { i_random = 0; } else { i_random = RandomInt( i_num_armor_pieces - 1 ); } i_random += 1; if( self.has_right_shoulder_armor === true && right_shoulder_index === i_random ) { self mechz_track_rshoulder_armor_damage( damage ); } if( self.has_left_shoulder_armor === true && left_shoulder_index === i_random ) { self mechz_track_lshoulder_armor_damage( damage ); } if( self.has_right_knee_armor === true && right_knee_index === i_random ) { self mechz_track_rknee_armor_damage( damage ); } if( self.has_left_knee_armor === true && left_knee_index === i_random ) { self mechz_track_lknee_armor_damage( damage ); } } else { if( self.powercap_covered === true ) { self mechz_track_powercap_cover_damage( damage * 0.5 ); } if( self.has_faceplate == true ) { self mechz_track_faceplate_damage( damage * 0.5 ); } } /#iPrintLnBold( "Projectile Splash DMG: " + hit_damage + ". HP: " + ( self.health - hit_damage ) );#/ return hit_damage; } return 0; } //used to step down the damage from some high-powered weapons function private mechzWeaponDamageModifier( damage, weapon ) { if( isDefined( weapon) && isDefined( weapon.name ) ) { if( isSubStr( weapon.name, "shotgun_fullauto") ) { return damage * 0.5; } if( isSubStr( weapon.name, "lmg_cqb") ) { return damage * 0.65; } if( isSubStr( weapon.name, "lmg_heavy") ) { return damage * 0.65; } if( isSubStr( weapon.name, "shotgun_precision") ) { return damage * 0.65; } if( isSubstr( weapon.name, "shotgun_semiauto") ) { return damage * 0.75; } } return damage; } function mechz_play_pain_audio() { self playsound( "zmb_ai_mechz_destruction" ); } function show_hit_marker() // self = player { if ( IsDefined( self ) && IsDefined( self.hud_damagefeedback ) ) { self.hud_damagefeedback SetShader( "damage_feedback", 24, 48 ); self.hud_damagefeedback.alpha = 1; self.hud_damagefeedback FadeOverTime(1); self.hud_damagefeedback.alpha = 0; } } function hide_part( strTag ) { if ( self HasPart(strTag) ) { self HidePart(strTag); } } function mechz_track_faceplate_damage( damage ) { self.faceplate_health = self.faceplate_health - damage; if( self.faceplate_health <= 0 ) { self hide_part( "j_faceplate" ); self clientfield::set( "mechz_faceplate_detached", 1 ); self.has_faceplate = false; self MechzServerUtils::mechz_turn_off_headlamp(); self.partDestroyed = true; Blackboard::SetBlackBoardAttribute( self, "_mechz_part", "mechz_faceplate" ); self MechzBehavior::mechzGoBerserk(); level notify( "mechz_faceplate_detached" ); } } function mechz_track_powercap_cover_damage( damage ) { self.powercap_cover_health = self.powercap_cover_health - damage; if( self.powercap_cover_health <= 0 ) { self hide_part( "tag_powersupply" ); self clientfield::set( "mechz_powercap_detached", 1 ); self.powercap_covered = false; self.partDestroyed = true; Blackboard::SetBlackBoardAttribute( self, "_mechz_part", "mechz_powercore" ); //self MechzBehavior::mechzGoBerserk(); } } function mechz_track_powercap_damage( damage ) { self.powercap_health = self.powercap_health - damage; if( self.powercap_health <=0 ) { if( IsDefined( level.mechz_powercap_destroyed_callback ) ) { self [[level.mechz_powercap_destroyed_callback]](); } self hide_part( "tag_gun_spin" ); self hide_part( "tag_gun_barrel1" ); self hide_part( "tag_gun_barrel2" ); self hide_part( "tag_gun_barrel3" ); self hide_part( "tag_gun_barrel4" ); self hide_part( "tag_gun_barrel5" ); self hide_part( "tag_gun_barrel6" ); self clientfield::set( "mechz_claw_detached", 1 ); self.has_powercap = false; self.gun_attached = false; self.partDestroyed = true; Blackboard::SetBlackBoardAttribute( self, "_mechz_part", "mechz_gun" ); //self MechzBehavior::mechzGoBerserk(); level notify( "mechz_gun_detached" ); } } function mechz_track_rknee_armor_damage( damage ) { self.right_knee_armor_health = self.right_knee_armor_health - damage; if( self.right_knee_armor_health <= 0 ) { self hide_part( "j_knee_attach_ri" ); self clientfield::set( "mechz_rknee_armor_detached", 1 ); self.has_right_knee_armor = false; } } function mechz_track_lknee_armor_damage( damage ) { self.left_knee_armor_health = self.left_knee_armor_health - damage; if( self.left_knee_armor_health <= 0 ) { self hide_part( "j_knee_attach_le" ); self clientfield::set( "mechz_lknee_armor_detached", 1 ); self.has_left_knee_armor = false; } } function mechz_track_rshoulder_armor_damage( damage ) { self.right_shoulder_armor_health = self.right_shoulder_armor_health - damage; if( self.right_shoulder_armor_health <= 0 ) { self hide_part( "j_shoulderarmor_ri" ); self clientfield::set( "mechz_rshoulder_armor_detached", 1 ); self.has_right_shoulder_armor = false; } } function mechz_track_lshoulder_armor_damage( damage ) { self.left_shoulder_armor_health = self.left_shoulder_armor_health - damage; if( self.left_shoulder_armor_health <= 0 ) { self hide_part( "j_shoulderarmor_le" ); self clientfield::set( "mechz_lshoulder_armor_detached", 1 ); self.has_left_shoulder_armor = false; } } function mechzCheckInArc( right_offset, aim_tag ) { origin = self.origin; angles = self.angles; if ( IsDefined( aim_tag ) ) { origin = self GetTagOrigin( aim_tag ); angles = self GetTagAngles( aim_tag ); } if ( IsDefined( right_offset ) ) { right_angle = anglestoright( angles ); origin = origin + (right_angle * right_offset); } facing_vec = AnglesToForward( angles ); enemy_vec = self.favoriteenemy.origin - origin; enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0); facing_yaw_vec = (facing_vec[0], facing_vec[1], 0); enemy_yaw_vec = VectorNormalize( enemy_yaw_vec ); facing_yaw_vec = VectorNormalize( facing_yaw_vec ); enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec ); if( enemy_dot < 0.5 ) { return false; } enemy_angles = VectorToAngles( enemy_vec ); if( abs( AngleClamp180( enemy_angles[0] ) ) > 60 ) { return false; } return true; } function private mechzGrenadeCheckInArc( right_offset ) { origin = self.origin; if ( IsDefined( right_offset ) ) { right_angle = anglestoright( self.angles ); origin = origin + (right_angle * right_offset); } facing_vec = AnglesToForward( self.angles ); enemy_vec = self.favoriteenemy.origin - origin; enemy_yaw_vec = (enemy_vec[0], enemy_vec[1], 0); facing_yaw_vec = (facing_vec[0], facing_vec[1], 0); enemy_yaw_vec = VectorNormalize( enemy_yaw_vec ); facing_yaw_vec = VectorNormalize( facing_yaw_vec ); enemy_dot = VectorDot( facing_yaw_vec, enemy_yaw_vec ); if( enemy_dot < 0.5 ) { return false; } enemy_angles = VectorToAngles( enemy_vec ); if( abs( AngleClamp180( enemy_angles[0] ) ) > 60 ) { return false; } return true; } function mechz_turn_off_headlamp( headlamp_broken ) { if( headlamp_broken !== true ) { self clientfield::set( "mechz_headlamp_off", 1 ); } else { self clientfield::set( "mechz_headlamp_off", 2 ); } }