1712 lines
59 KiB
Plaintext
1712 lines
59 KiB
Plaintext
#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 );
|
|
}
|
|
}
|