1590 lines
54 KiB
Plaintext
1590 lines
54 KiB
Plaintext
#using scripts\shared\ai_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\array_shared;
|
|
|
|
#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\margwa;
|
|
#using scripts\shared\ai\zombie_utility;
|
|
#using scripts\codescripts\struct;
|
|
#using scripts\shared\ai\archetype_mocomps_utility;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#namespace MargwaBehavior;
|
|
|
|
function autoexec init()
|
|
{
|
|
// INIT BEHAVIORS
|
|
InitMargwaBehaviorsAndASM();
|
|
|
|
// INIT BLACKBOARD
|
|
spawner::add_archetype_spawn_function( "margwa", &ArchetypeMargwaBlackboardInit );
|
|
|
|
// INIT MARGWA ON SPAWN
|
|
spawner::add_archetype_spawn_function( "margwa", &MargwaServerUtils::margwaSpawnSetup );
|
|
|
|
clientfield::register( "actor", "margwa_head_left", 1, 2, "int" );
|
|
clientfield::register( "actor", "margwa_head_mid", 1, 2, "int" );
|
|
clientfield::register( "actor", "margwa_head_right", 1, 2, "int" );
|
|
clientfield::register( "actor", "margwa_fx_in", 1, 1, "counter" );
|
|
clientfield::register( "actor", "margwa_fx_out", 1, 1, "counter" );
|
|
clientfield::register( "actor", "margwa_fx_spawn", 1, 1, "counter" );
|
|
clientfield::register( "actor", "margwa_smash", 1, 1, "counter" );
|
|
clientfield::register( "actor", "margwa_head_left_hit", 1, 1, "counter" );
|
|
clientfield::register( "actor", "margwa_head_mid_hit", 1, 1, "counter" );
|
|
clientfield::register( "actor", "margwa_head_right_hit", 1, 1, "counter" );
|
|
|
|
clientfield::register( "actor", "margwa_head_killed", 1, 2, "int" );
|
|
clientfield::register( "actor", "margwa_jaw", 1, 6, "int" );
|
|
|
|
clientfield::register( "toplayer", "margwa_head_explosion", 1, 1, "counter" );
|
|
clientfield::register( "scriptmover", "margwa_fx_travel", 1, 1, "int" );
|
|
clientfield::register( "scriptmover", "margwa_fx_travel_tell", 1, 1, "int" );
|
|
|
|
clientfield::register( "actor", "supermargwa", 1, 1, "int" ); // set this bit when spawning supermargwa for ee quest
|
|
|
|
InitDirectHitWeapons();
|
|
}
|
|
|
|
function private InitDirectHitWeapons()
|
|
{
|
|
if ( !IsDefined( level.dhWeapons ) )
|
|
{
|
|
level.dhWeapons = [];
|
|
}
|
|
|
|
level.dhWeapons[ level.dhWeapons.size ] = "ray_gun";
|
|
level.dhWeapons[ level.dhWeapons.size ] = "ray_gun_upgraded";
|
|
level.dhWeapons[ level.dhWeapons.size ] = "pistol_standard_upgraded";
|
|
level.dhWeapons[ level.dhWeapons.size ] = "pistol_revolver38_upgraded";
|
|
level.dhWeapons[ level.dhWeapons.size ] = "pistol_revolver38lh_upgraded";
|
|
level.dhWeapons[ level.dhWeapons.size ] = "launcher_standard";
|
|
level.dhWeapons[ level.dhWeapons.size ] = "launcher_standard_upgraded";
|
|
}
|
|
|
|
function AddDirectHitWeapon( weaponName )
|
|
{
|
|
foreach( weapon in level.dhWeapons )
|
|
{
|
|
if ( weapon == weaponName )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
level.dhWeapons[ level.dhWeapons.size ] = weaponName;
|
|
}
|
|
|
|
function private InitMargwaBehaviorsAndASM()
|
|
{
|
|
// SERVICES
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaTargetService",&MargwaBehavior::margwaTargetService);;
|
|
|
|
// CONDITIONS
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldSmashAttack",&MargwaBehavior::margwaShouldSmashAttack);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldSwipeAttack",&MargwaBehavior::margwaShouldSwipeAttack);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldShowPain",&MargwaBehavior::margwaShouldShowPain);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldReactStun",&MargwaBehavior::margwaShouldReactStun);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldReactIDGun",&MargwaBehavior::margwaShouldReactIDGun);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldReactSword",&MargwaBehavior::margwaShouldReactSword);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldSpawn",&MargwaBehavior::margwaShouldSpawn);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldFreeze",&MargwaBehavior::margwaShouldFreeze);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldTeleportIn",&MargwaBehavior::margwaShouldTeleportIn);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldTeleportOut",&MargwaBehavior::margwaShouldTeleportOut);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldWait",&MargwaBehavior::margwaShouldWait);;
|
|
|
|
// Consolidated condition checking to limit VM calls
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaShouldReset",&MargwaBehavior::margwaShouldReset);;
|
|
|
|
// ACTIONS
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("margwaReactStunAction",&MargwaBehavior::margwaReactStunAction,undefined,undefined);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("margwaSwipeAttackAction",&MargwaBehavior::margwaSwipeAttackAction,&MargwaBehavior::margwaSwipeAttackActionUpdate,undefined);;
|
|
|
|
// FUNCTIONS
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaIdleStart",&MargwaBehavior::margwaIdleStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaMoveStart",&MargwaBehavior::margwaMoveStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaTraverseActionStart",&MargwaBehavior::margwaTraverseActionStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaTeleportInStart",&MargwaBehavior::margwaTeleportInStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaTeleportInTerminate",&MargwaBehavior::margwaTeleportInTerminate);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaTeleportOutStart",&MargwaBehavior::margwaTeleportOutStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaTeleportOutTerminate",&MargwaBehavior::margwaTeleportOutTerminate);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaPainStart",&MargwaBehavior::margwaPainStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaPainTerminate",&MargwaBehavior::margwaPainTerminate);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaReactStunStart",&MargwaBehavior::margwaReactStunStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaReactStunTerminate",&MargwaBehavior::margwaReactStunTerminate);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaReactIDGunStart",&MargwaBehavior::margwaReactIDGunStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaReactIDGunTerminate",&MargwaBehavior::margwaReactIDGunTerminate);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaReactSwordStart",&MargwaBehavior::margwaReactSwordStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaReactSwordTerminate",&MargwaBehavior::margwaReactSwordTerminate);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaSpawnStart",&MargwaBehavior::margwaSpawnStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaSmashAttackStart",&MargwaBehavior::margwaSmashAttackStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaSmashAttackTerminate",&MargwaBehavior::margwaSmashAttackTerminate);;
|
|
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaSwipeAttackStart",&MargwaBehavior::margwaSwipeAttackStart);;
|
|
BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("margwaSwipeAttackTerminate",&MargwaBehavior::margwaSwipeAttackTerminate);;
|
|
|
|
// MOCOMPS
|
|
AnimationStateNetwork::RegisterAnimationMocomp("mocomp_teleport_traversal@margwa",&mocompMargwaTeleportTraversalInit,&mocompMargwaTeleportTraversalUpdate,&mocompMargwaTeleportTraversalTerminate);;
|
|
|
|
// NOTETRACKS
|
|
AnimationStateNetwork::RegisterNotetrackHandlerFunction("margwa_smash_attack",&MargwaBehavior::margwaNotetrackSmashAttack);;
|
|
AnimationStateNetwork::RegisterNotetrackHandlerFunction("margwa_bodyfall large",&MargwaBehavior::margwaNotetrackBodyfall);;
|
|
AnimationStateNetwork::RegisterNotetrackHandlerFunction("margwa_melee_fire",&MargwaBehavior::margwaNotetrackPainMelee);;
|
|
}
|
|
|
|
function private ArchetypeMargwaBlackboardInit()
|
|
{
|
|
// CREATE BLACKBOARD
|
|
Blackboard::CreateBlackBoardForEntity( self );
|
|
|
|
// USE UTILITY BLACKBOARD
|
|
self AiUtility::RegisterUtilityBlackboardAttributes();
|
|
|
|
// CREATE MARGWA BLACKBOARD
|
|
Blackboard::RegisterBlackBoardAttribute(self,"_locomotion_speed","locomotion_speed_walk",undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_locomotion_speed");#/ };
|
|
Blackboard::RegisterBlackBoardAttribute(self,"_board_attack_spot",undefined,undefined); if( IsActor(self) ) { /#self TrackBlackBoardAttribute("_board_attack_spot");#/ };
|
|
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");#/ };
|
|
|
|
// REGISTER ANIMSCRIPTED CALLBACK
|
|
self.___ArchetypeOnAnimscriptedCallback = &ArchetypeMargwaOnAnimscriptedCallback;
|
|
|
|
// ENABLE DEBUGGING IN ODYSSEY
|
|
/#self FinalizeTrackedBlackboardAttributes();#/;
|
|
}
|
|
|
|
function private ArchetypeMargwaOnAnimscriptedCallback( entity )
|
|
{
|
|
// UNREGISTER THE BLACKBOARD
|
|
entity.__blackboard = undefined;
|
|
|
|
// REREGISTER BLACKBOARD
|
|
entity ArchetypeMargwaBlackboardInit();
|
|
}
|
|
|
|
function private BB_GetShouldTurn()
|
|
{
|
|
if( IsDefined( self.should_turn ) && self.should_turn )
|
|
{
|
|
return "should_turn";
|
|
}
|
|
return "should_not_turn";
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------------
|
|
// NOTETRACK HANDLERS
|
|
//----------------------------------------------------------------------------------------------------------------------------
|
|
function private margwaNotetrackSmashAttack( entity )
|
|
{
|
|
players = GetPlayers();
|
|
foreach( player in players )
|
|
{
|
|
smashPos = entity.origin + VectorScale( AnglesToForward( self.angles ), 60 );
|
|
distSq = DistanceSquared( smashPos, player.origin );
|
|
if ( distSq < 144 * 144 )
|
|
{
|
|
if ( !IsGodMode( player ) )
|
|
{
|
|
// riot shield can block damage from front or back
|
|
if ( ( isdefined( player.hasRiotShield ) && player.hasRiotShield ) )
|
|
{
|
|
damageShield = false;
|
|
attackDir = player.origin - self.origin;
|
|
|
|
if ( ( isdefined( player.hasRiotShieldEquipped ) && player.hasRiotShieldEquipped ) )
|
|
{
|
|
if ( player margwaServerUtils::shieldFacing( attackDir, 0.2 ) )
|
|
{
|
|
damageShield = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( player margwaServerUtils::shieldFacing( attackDir, 0.2, false ) )
|
|
{
|
|
damageShield = true;
|
|
}
|
|
}
|
|
|
|
if ( damageShield )
|
|
{
|
|
self clientfield::increment( "margwa_smash" );
|
|
shield_damage = level.weaponRiotshield.weaponstarthitpoints;
|
|
if ( IsDefined( player.weaponRiotshield ) )
|
|
shield_damage = player.weaponRiotshield.weaponstarthitpoints;
|
|
player [[ player.player_shield_apply_damage ]]( shield_damage, false );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( isdefined( level.margwa_smash_damage_callback ) && IsFunctionPtr( level.margwa_smash_damage_callback ) )
|
|
{
|
|
if ( player [[ level.margwa_smash_damage_callback ]]( self ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
self clientfield::increment( "margwa_smash" );
|
|
player DoDamage( 166, self.origin, self );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( self.smashAttackCB ) )
|
|
{
|
|
self [[ self.smashAttackCB ]]();
|
|
}
|
|
}
|
|
|
|
// Fx takes over after margwa hits the ground
|
|
function private margwaNotetrackBodyfall( entity )
|
|
{
|
|
if( self.archetype == "margwa" )
|
|
{
|
|
entity Ghost();
|
|
|
|
if ( IsDefined( self.bodyfallCB ) )
|
|
{
|
|
self [[ self.bodyfallCB ]]();
|
|
}
|
|
}
|
|
}
|
|
|
|
function private margwaNotetrackPainMelee( entity )
|
|
{
|
|
entity Melee();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------------
|
|
// BEHAVIOR TREE
|
|
//----------------------------------------------------------------------------------------------------------------------------
|
|
function private margwaTargetService( entity )
|
|
{
|
|
if ( ( isdefined( entity.ignoreall ) && entity.ignoreall ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
player = zombie_utility::get_closest_valid_player( self.origin, self.ignore_player );
|
|
|
|
if( !IsDefined( player ) )
|
|
{
|
|
if( IsDefined( self.ignore_player ) )
|
|
{
|
|
if(isDefined(level._should_skip_ignore_player_logic) && [[level._should_skip_ignore_player_logic]]() )
|
|
{
|
|
return;
|
|
}
|
|
self.ignore_player = [];
|
|
}
|
|
|
|
self SetGoal( self.origin );
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
targetPos = GetClosestPointOnNavMesh( player.origin, 64, 30 );
|
|
if ( IsDefined( targetPos ) )
|
|
{
|
|
entity SetGoal( targetPos );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
entity SetGoal( entity.origin );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
function margwaShouldSmashAttack( entity )
|
|
{
|
|
if( !IsDefined( entity.enemy ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !entity MargwaServerUtils::inSmashAttackRange( entity.enemy ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
yaw = abs( zombie_utility::getYawToEnemy() );
|
|
if( ( yaw > 45 ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function margwaShouldSwipeAttack( entity )
|
|
{
|
|
if( !IsDefined( entity.enemy ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( DistanceSquared( entity.origin, entity.enemy.origin ) > 128 * 128 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
yaw = abs( zombie_utility::getYawToEnemy() );
|
|
if( ( yaw > 45 ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function private margwaShouldShowPain( entity )
|
|
{
|
|
if ( IsDefined( entity.headDestroyed ) )
|
|
{
|
|
headInfo = entity.head[ entity.headDestroyed ];
|
|
switch( headInfo.cf )
|
|
{
|
|
case "margwa_head_left":
|
|
Blackboard::SetBlackBoardAttribute( self, "_margwa_head", "left" );
|
|
break;
|
|
|
|
case "margwa_head_mid":
|
|
Blackboard::SetBlackBoardAttribute( self, "_margwa_head", "middle" );
|
|
break;
|
|
|
|
case "margwa_head_right":
|
|
Blackboard::SetBlackBoardAttribute( self, "_margwa_head", "right" );
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldReactStun( entity )
|
|
{
|
|
if ( ( isdefined( entity.reactStun ) && entity.reactStun ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldReactIDGun( entity )
|
|
{
|
|
if ( ( isdefined( entity.reactIDGun ) && entity.reactIDGun ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldReactSword( entity )
|
|
{
|
|
if ( ( isdefined( entity.reactSword ) && entity.reactSword ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldSpawn( entity )
|
|
{
|
|
if ( ( isdefined( entity.needSpawn ) && entity.needSpawn ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldFreeze( entity )
|
|
{
|
|
if ( ( isdefined( entity.isFrozen ) && entity.isFrozen ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldTeleportIn( entity )
|
|
{
|
|
if ( ( isdefined( entity.needTeleportIn ) && entity.needTeleportIn ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldTeleportOut( entity )
|
|
{
|
|
if ( ( isdefined( entity.needTeleportOut ) && entity.needTeleportOut ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldWait( entity )
|
|
{
|
|
if ( ( isdefined( entity.waiting ) && entity.waiting ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaShouldReset( entity )
|
|
{
|
|
if ( IsDefined( entity.headDestroyed ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( ( isdefined( entity.reactIDGun ) && entity.reactIDGun ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( ( isdefined( entity.reactSword ) && entity.reactSword ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( ( isdefined( entity.reactStun ) && entity.reactStun ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------------------
|
|
// ACTIONS
|
|
//----------------------------------------------------------------------------------------------------------------------------------
|
|
function private margwaReactStunAction( entity, asmStateName )
|
|
{
|
|
AnimationStateNetworkUtility::RequestState( entity, asmStateName );
|
|
|
|
stunActionAST = entity ASTSearch( IString( asmStateName ) );
|
|
stunActionAnimation = AnimationStateNetworkUtility::SearchAnimationMap( entity, stunActionAST[ "animation" ] );
|
|
|
|
closeTime = GetAnimLength( stunActionAnimation ) * 1000;
|
|
|
|
entity MargwaServerUtils::margwaCloseAllHeads( closeTime );
|
|
|
|
MargwaBehavior::margwaReactStunStart( entity );
|
|
|
|
return 5;
|
|
}
|
|
|
|
|
|
function private margwaSwipeAttackAction( entity, asmStateName )
|
|
{
|
|
AnimationStateNetworkUtility::RequestState( entity, asmStateName );
|
|
|
|
if ( !isdefined( entity.swipe_end_time ) )
|
|
{
|
|
swipeActionAST = entity ASTSearch( IString( asmStateName ) );
|
|
swipeActionAnimation = AnimationStateNetworkUtility::SearchAnimationMap( entity, swipeActionAST[ "animation" ] );
|
|
swipeActionTime = GetAnimLength( swipeActionAnimation ) * 1000;
|
|
|
|
entity.swipe_end_time = GetTime() + swipeActionTime;
|
|
}
|
|
|
|
return 5;
|
|
}
|
|
|
|
function private margwaSwipeAttackActionUpdate( entity, asmStateName )
|
|
{
|
|
if ( isdefined( entity.swipe_end_time ) && GetTime() > entity.swipe_end_time )
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
return 5;
|
|
}
|
|
|
|
function private margwaIdleStart( entity )
|
|
{
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 1 );
|
|
}
|
|
}
|
|
|
|
function private margwaMoveStart( entity )
|
|
{
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
if ( entity.zombie_move_speed == "run" )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 13 );
|
|
}
|
|
else
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 7 );
|
|
}
|
|
}
|
|
}
|
|
|
|
function private margwaDeathAction( entity )
|
|
{
|
|
//insert anything that needs to be done right before zombie death
|
|
}
|
|
|
|
function private margwaTraverseActionStart( entity )
|
|
{
|
|
Blackboard::SetBlackBoardAttribute( entity, "_traversal_type", entity.traverseStartNode.animscript );
|
|
|
|
if( isdefined( entity.traverseStartNode.animscript ) )
|
|
{
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
switch ( entity.traverseStartNode.animscript )
|
|
{
|
|
case "jump_down_36":
|
|
entity clientfield::set( "margwa_jaw", 21 );
|
|
break;
|
|
|
|
case "jump_down_96":
|
|
entity clientfield::set( "margwa_jaw", 22 );
|
|
break;
|
|
|
|
case "jump_up_36":
|
|
entity clientfield::set( "margwa_jaw", 24 );
|
|
break;
|
|
|
|
case "jump_up_96":
|
|
entity clientfield::set( "margwa_jaw", 25 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function private margwaTeleportInStart( entity )
|
|
{
|
|
entity Unlink();
|
|
if ( IsDefined( entity.teleportPos ) )
|
|
{
|
|
entity ForceTeleport( entity.teleportPos );
|
|
}
|
|
entity Show();
|
|
entity PathMode( "move allowed" );
|
|
entity.needTeleportIn = false;
|
|
Blackboard::SetBlackBoardAttribute( self, "_margwa_teleport", "in" );
|
|
|
|
if ( isdefined( self.traveler ) )
|
|
{
|
|
self.traveler clientfield::set( "margwa_fx_travel", 0 );
|
|
}
|
|
self clientfield::increment( "margwa_fx_in", 1 );
|
|
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 17 );
|
|
}
|
|
}
|
|
|
|
function margwaTeleportInTerminate( entity )
|
|
{
|
|
if ( isdefined( self.traveler ) )
|
|
{
|
|
self.traveler clientfield::set( "margwa_fx_travel", 0 );
|
|
}
|
|
entity.isTeleporting = false;
|
|
}
|
|
|
|
function private margwaTeleportOutStart( entity )
|
|
{
|
|
entity.needTeleportOut = false;
|
|
entity.isTeleporting = true;
|
|
entity.teleportStart = entity.origin;
|
|
|
|
Blackboard::SetBlackBoardAttribute( self, "_margwa_teleport", "out" );
|
|
self clientfield::increment( "margwa_fx_out", 1 );
|
|
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 18 );
|
|
}
|
|
}
|
|
|
|
function private margwaTeleportOutTerminate( entity )
|
|
{
|
|
if ( isdefined( entity.traveler ) )
|
|
{
|
|
entity.traveler.origin = entity GetTagOrigin( "j_spine_1" );
|
|
entity.traveler clientfield::set( "margwa_fx_travel", 1 );
|
|
}
|
|
|
|
entity Ghost();
|
|
entity PathMode( "dont move" );
|
|
|
|
if ( isdefined( entity.traveler ) )
|
|
{
|
|
entity LinkTo( entity.traveler );
|
|
}
|
|
|
|
if ( isdefined( entity.margwaWait ) )
|
|
{
|
|
entity thread [[ entity.margwaWait ]]();
|
|
}
|
|
else
|
|
{
|
|
entity thread MargwaServerUtils::margwaWait();
|
|
}
|
|
}
|
|
|
|
function private margwaPainStart( entity )
|
|
{
|
|
entity notify( "stop_head_update" );
|
|
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
head = Blackboard::GetBlackBoardAttribute( self, "_margwa_head" );
|
|
switch ( head )
|
|
{
|
|
case "left":
|
|
entity clientfield::set( "margwa_jaw", 3 );
|
|
break;
|
|
|
|
case "middle":
|
|
entity clientfield::set( "margwa_jaw", 4 );
|
|
break;
|
|
|
|
case "right":
|
|
entity clientfield::set( "margwa_jaw", 5 );
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
entity.headDestroyed = undefined;
|
|
entity.canStun = false;
|
|
entity.canDamage = false;
|
|
}
|
|
|
|
function private margwaPainTerminate( entity )
|
|
{
|
|
entity.headDestroyed = undefined;
|
|
entity.canStun = true;
|
|
entity.canDamage = true;
|
|
|
|
entity MargwaServerUtils::margwaCloseAllHeads( 5000 );
|
|
|
|
entity ClearPath();
|
|
|
|
if ( IsDefined( entity.margwaPainTerminateCB ) )
|
|
{
|
|
entity [[ entity.margwaPainTerminateCB ]]();
|
|
}
|
|
}
|
|
|
|
function private margwaReactStunStart( entity )
|
|
{
|
|
entity.reactStun = undefined;
|
|
entity.canStun = false;
|
|
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 6 );
|
|
}
|
|
}
|
|
|
|
function margwaReactStunTerminate( entity )
|
|
{
|
|
entity.canStun = true;
|
|
}
|
|
|
|
function private margwaReactIDGunStart( entity )
|
|
{
|
|
entity.reactIDGun = undefined;
|
|
entity.canStun = false;
|
|
|
|
isPacked = false;
|
|
|
|
if( BlackBoard::GetBlackBoardAttribute( entity, "_zombie_damageweapon_type" ) == "regular" )
|
|
{
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 8 );
|
|
}
|
|
entity MargwaServerUtils::margwaCloseAllHeads( 5000 );
|
|
}
|
|
else
|
|
{
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 9 );
|
|
}
|
|
entity MargwaServerUtils::margwaCloseAllHeads( 2 * 5000 );
|
|
|
|
isPacked = true;
|
|
}
|
|
|
|
if ( IsDefined( entity.idgun_damage ) )
|
|
{
|
|
entity [[ entity.idgun_damage ]]( isPacked );
|
|
}
|
|
}
|
|
|
|
function margwaReactIDGunTerminate( entity )
|
|
{
|
|
entity.canStun = true;
|
|
Blackboard::SetBlackBoardAttribute( entity, "_zombie_damageweapon_type", "regular" );
|
|
}
|
|
|
|
function private margwaReactSwordStart( entity )
|
|
{
|
|
entity.reactSword = undefined;
|
|
entity.canStun = false;
|
|
|
|
if ( IsDefined( entity.head_chopper ) )
|
|
{
|
|
entity.head_chopper notify( "react_sword" );
|
|
}
|
|
}
|
|
|
|
function private margwaReactSwordTerminate( entity )
|
|
{
|
|
entity.canStun = true;
|
|
}
|
|
|
|
function private margwaSpawnStart( entity )
|
|
{
|
|
entity.needSpawn = false;
|
|
}
|
|
|
|
function private margwaSmashAttackStart( entity )
|
|
{
|
|
entity MargwaServerUtils::margwaHeadSmash();
|
|
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 14 );
|
|
}
|
|
}
|
|
|
|
function margwaSmashAttackTerminate( entity )
|
|
{
|
|
entity MargwaServerUtils::margwaCloseAllHeads();
|
|
}
|
|
|
|
function margwaSwipeAttackStart( entity )
|
|
{
|
|
if ( entity MargwaServerUtils::shouldUpdateJaw() )
|
|
{
|
|
entity clientfield::set( "margwa_jaw", 16 );
|
|
}
|
|
}
|
|
|
|
function private margwaSwipeattackTerminate( entity )
|
|
{
|
|
entity MargwaServerUtils::margwaCloseAllHeads();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// MOCOMPS
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
function private mocompMargwaTeleportTraversalInit( entity, mocompAnim, mocompAnimBlendOutTime, mocompAnimFlag, mocompDuration )
|
|
{
|
|
entity OrientMode( "face angle", entity.angles[1] );
|
|
entity AnimMode( "normal" );
|
|
|
|
if ( isdefined( entity.traverseEndNode ) )
|
|
{
|
|
entity.teleportStart = entity.origin;
|
|
entity.teleportPos = entity.traverseEndNode.origin;
|
|
self clientfield::increment( "margwa_fx_out", 1 );
|
|
|
|
if ( isdefined( entity.traverseStartNode ) )
|
|
{
|
|
if ( isdefined( entity.traverseStartNode.speed ) )
|
|
{
|
|
self.margwa_teleport_speed = entity.traverseStartNode.speed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function private mocompMargwaTeleportTraversalUpdate( entity, mocompAnim, mocompAnimBlendOutTime, mocompAnimFlag, mocompDuration )
|
|
{
|
|
}
|
|
|
|
function private mocompMargwaTeleportTraversalTerminate( entity, mocompAnim, mocompAnimBlendOutTime, mocompAnimFlag, mocompDuration )
|
|
{
|
|
margwaTeleportOutTerminate( entity );
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#namespace MargwaServerUtils;
|
|
|
|
function private margwaSpawnSetup()
|
|
{
|
|
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 = "walk";
|
|
|
|
self.overrideActorDamage = &margwaDamage;
|
|
self.canDamage = true;
|
|
|
|
self.headAttached = 3;
|
|
self.headOpen = 0;
|
|
|
|
self margwaInitHead( "c_zom_margwa_chunks_le", "j_chunk_head_bone_le" );
|
|
self margwaInitHead( "c_zom_margwa_chunks_mid", "j_chunk_head_bone" );
|
|
self margwaInitHead( "c_zom_margwa_chunks_ri", "j_chunk_head_bone_ri" );
|
|
|
|
self.headHealthMax = 600;
|
|
|
|
self margwaDisableStun();
|
|
|
|
self.traveler = Spawn( "script_model", self.origin );
|
|
self.traveler SetModel( "tag_origin" );
|
|
self.traveler NotSolid();
|
|
|
|
self.travelerTell = Spawn( "script_model", self.origin );
|
|
self.travelerTell SetModel( "tag_origin" );
|
|
self.travelerTell NotSolid();
|
|
|
|
self thread margwaDeath();
|
|
|
|
self.updateSight = false;
|
|
self.ignoreRunAndGunDist = true;
|
|
}
|
|
|
|
function private margwaDeath()
|
|
{
|
|
self waittill( "death" );
|
|
|
|
if( isdefined(self.e_head_attacker) )
|
|
{
|
|
self.e_head_attacker notify( "margwa_kill" );
|
|
}
|
|
|
|
if ( IsDefined( self.traveler ) )
|
|
{
|
|
self.traveler Delete();
|
|
}
|
|
|
|
if ( IsDefined( self.travelerTell ) )
|
|
{
|
|
self.travelerTell Delete();
|
|
}
|
|
}
|
|
|
|
function margwaEnableStun()
|
|
{
|
|
self.canStun = true;
|
|
}
|
|
|
|
function private margwaDisableStun()
|
|
{
|
|
self.canStun = false;
|
|
}
|
|
|
|
function private margwaInitHead( headModel, headTag )
|
|
{
|
|
model = headModel;
|
|
model_gore = undefined;
|
|
|
|
switch ( headModel )
|
|
{
|
|
case "c_zom_margwa_chunks_le":
|
|
if( isdefined( level.margwa_head_left_model_override ))
|
|
{
|
|
model = level.margwa_head_left_model_override;
|
|
model_gore = level.margwa_gore_left_model_override;
|
|
}
|
|
break;
|
|
|
|
case "c_zom_margwa_chunks_mid":
|
|
if( isdefined( level.margwa_head_mid_model_override ))
|
|
{
|
|
model = level.margwa_head_mid_model_override;
|
|
model_gore = level.margwa_gore_mid_model_override;
|
|
}
|
|
break;
|
|
|
|
case "c_zom_margwa_chunks_ri":
|
|
if( isdefined( level.margwa_head_right_model_override ))
|
|
{
|
|
model = level.margwa_head_right_model_override;
|
|
model_gore = level.margwa_gore_right_model_override;
|
|
}
|
|
break;
|
|
}
|
|
|
|
self Attach( model );
|
|
|
|
if ( !IsDefined( self.head ) )
|
|
{
|
|
self.head = [];
|
|
}
|
|
|
|
self.head[ model ] = SpawnStruct();
|
|
self.head[ model ].model = model;
|
|
self.head[ model ].tag = headTag;
|
|
self.head[ model ].health = 600;
|
|
self.head[ model ].canDamage = false;
|
|
|
|
self.head[ model ].open = 1;
|
|
self.head[ model ].closed = 2;
|
|
self.head[ model ].smash = 3;
|
|
|
|
switch ( headModel )
|
|
{
|
|
case "c_zom_margwa_chunks_le":
|
|
self.head[ model ].cf = "margwa_head_left";
|
|
self.head[ model ].impactCF = "margwa_head_left_hit";
|
|
self.head[ model ].gore = "c_zom_margwa_gore_le";
|
|
if( isdefined( model_gore ))
|
|
{
|
|
self.head[ model ].gore = model_gore;
|
|
}
|
|
self.head[ model ].killIndex = 1;
|
|
self.head_left_model = model;
|
|
break;
|
|
|
|
case "c_zom_margwa_chunks_mid":
|
|
self.head[ model ].cf = "margwa_head_mid";
|
|
self.head[ model ].impactCF = "margwa_head_mid_hit";
|
|
self.head[ model ].gore = "c_zom_margwa_gore_mid";
|
|
if( isdefined( model_gore ))
|
|
{
|
|
self.head[ model ].gore = model_gore;
|
|
}
|
|
self.head[ model ].killIndex = 2;
|
|
self.head_mid_model = model;
|
|
break;
|
|
|
|
case "c_zom_margwa_chunks_ri":
|
|
self.head[ model ].cf = "margwa_head_right";
|
|
self.head[ model ].impactCF = "margwa_head_right_hit";
|
|
self.head[ model ].gore = "c_zom_margwa_gore_ri";
|
|
if( isdefined( model_gore ))
|
|
{
|
|
self.head[ model ].gore = model_gore;
|
|
}
|
|
self.head[ model ].killIndex = 3;
|
|
self.head_right_model = model;
|
|
break;
|
|
}
|
|
|
|
self thread margwaHeadUpdate( self.head[ model ] );
|
|
}
|
|
|
|
function margwaSetHeadHealth( health )
|
|
{
|
|
self.headHealthMax = health;
|
|
|
|
foreach( head in self.head )
|
|
{
|
|
head.health = health;
|
|
}
|
|
}
|
|
|
|
function private margwaResetHeadTime( min, max )
|
|
{
|
|
time = GetTime() + RandomIntRange( min, max );
|
|
return time;
|
|
}
|
|
|
|
function private margwaHeadCanOpen()
|
|
{
|
|
if ( self.headAttached > 1 )
|
|
{
|
|
if ( self.headOpen < (self.headAttached - 1) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function private margwaHeadUpdate( headInfo )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "stop_head_update" );
|
|
|
|
headInfo notify( "stop_head_update" );
|
|
headInfo endon( "stop_head_update" );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( self IsPaused() )
|
|
{
|
|
util::wait_network_frame();
|
|
continue;
|
|
}
|
|
|
|
if ( !IsDefined( headInfo.closeTime ) )
|
|
{
|
|
if ( self.headAttached == 1 )
|
|
{
|
|
headInfo.closeTime = margwaResetHeadTime( 500, 1000 );
|
|
}
|
|
else
|
|
{
|
|
headInfo.closeTime = margwaResetHeadTime( 1500, 3500 );
|
|
}
|
|
}
|
|
|
|
if ( GetTime() > headInfo.closeTime && self margwaHeadCanOpen() )
|
|
{
|
|
self.headOpen++;
|
|
headInfo.closeTime = undefined;
|
|
}
|
|
else
|
|
{
|
|
util::wait_network_frame();
|
|
continue;
|
|
}
|
|
|
|
self margwaHeadDamageDelay( headInfo, true );
|
|
self clientfield::set( headInfo.cf, headInfo.open );
|
|
self playsoundontag( "zmb_vocals_margwa_ambient", headInfo.tag );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( !IsDefined( headInfo.openTime ) )
|
|
{
|
|
headInfo.openTime = margwaResetHeadTime( 3000, 5000 );
|
|
}
|
|
|
|
if ( GetTime() > headInfo.openTime )
|
|
{
|
|
self.headOpen--;
|
|
headInfo.openTime = undefined;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
util::wait_network_frame();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
self margwaHeadDamageDelay( headInfo, false );
|
|
self clientfield::set( headInfo.cf, headInfo.closed );
|
|
}
|
|
}
|
|
|
|
function private margwaHeadDamageDelay( headInfo, canDamage )
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait( 0.1 );
|
|
|
|
headInfo.canDamage = canDamage;
|
|
}
|
|
|
|
function private margwaHeadSmash()
|
|
{
|
|
self notify( "stop_head_update" );
|
|
|
|
headAlive = [];
|
|
foreach( head in self.head )
|
|
{
|
|
if ( head.health > 0 )
|
|
{
|
|
headAlive[ headAlive.size ] = head;
|
|
}
|
|
}
|
|
|
|
headAlive = array::randomize( headAlive );
|
|
open = false;
|
|
|
|
foreach( head in headAlive )
|
|
{
|
|
if ( !open )
|
|
{
|
|
head.canDamage = true;
|
|
self clientfield::set( head.cf, head.smash );
|
|
open = true;
|
|
}
|
|
else
|
|
{
|
|
self margwaCloseHead( head );
|
|
}
|
|
}
|
|
}
|
|
|
|
function private margwaCloseHead( headInfo )
|
|
{
|
|
headInfo.canDamage = false;
|
|
self clientfield::set( headInfo.cf, headInfo.closed );
|
|
}
|
|
|
|
function private margwaCloseAllHeads( closeTime )
|
|
{
|
|
if ( self IsPaused() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach ( head in self.head )
|
|
{
|
|
if ( head.health > 0 )
|
|
{
|
|
head.closeTime = undefined;
|
|
head.openTime = undefined;
|
|
|
|
if ( IsDefined( closeTime ) )
|
|
{
|
|
head.closeTime = GetTime() + closeTime;
|
|
}
|
|
|
|
self.headOpen = 0;
|
|
|
|
self margwaCloseHead( head );
|
|
self thread margwaHeadUpdate( head );
|
|
}
|
|
}
|
|
}
|
|
|
|
function margwaKillHead( modelHit, attacker )
|
|
{
|
|
headInfo = self.head[ modelHit ];
|
|
|
|
headInfo.health = 0;
|
|
headInfo notify( "stop_head_update" );
|
|
|
|
if ( ( isdefined( headInfo.canDamage ) && headInfo.canDamage ) )
|
|
{
|
|
self margwaCloseHead( headInfo );
|
|
self.headOpen--;
|
|
}
|
|
|
|
self margwaUpdateMoveSpeed();
|
|
|
|
if ( IsDefined( self.destroyHeadCB ) )
|
|
{
|
|
self thread [[ self.destroyHeadCB ]]( modelHit, attacker );
|
|
}
|
|
|
|
self clientfield::set( "margwa_head_killed", headInfo.killIndex );
|
|
|
|
self Detach( headInfo.model );
|
|
self Attach( headInfo.gore );
|
|
self.headAttached--;
|
|
|
|
if ( self.headAttached <= 0 )
|
|
{
|
|
self.e_head_attacker = attacker;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
self.headDestroyed = modelHit;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function margwaCanDamageAnyHead()
|
|
{
|
|
foreach( head in self.head )
|
|
{
|
|
if ( IsDefined( head ) && head.health > 0 && ( isdefined( head.canDamage ) && head.canDamage ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function margwaCanDamageHead()
|
|
{
|
|
if ( IsDefined( self ) && self.health > 0 && ( isdefined( self.canDamage ) && self.canDamage ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
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 private isDirectHitWeapon( weapon )
|
|
{
|
|
foreach( dhWeapon in level.dhWeapons )
|
|
{
|
|
if ( weapon.name == dhWeapon )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( isdefined( weapon.rootweapon ) && isdefined( weapon.rootweapon.name ) && weapon.rootweapon.name == dhWeapon )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// uses the bone name to figure out which head was hit
|
|
function margwaDamage( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex )
|
|
{
|
|
if ( ( isdefined( self.is_kill ) && self.is_kill ) )
|
|
{
|
|
return damage;
|
|
}
|
|
|
|
if( isdefined(attacker) && isdefined(attacker.n_margwa_head_damage_scale) )
|
|
{
|
|
damage = damage * attacker.n_margwa_head_damage_scale;
|
|
}
|
|
|
|
if( isdefined( level._margwa_damage_cb ) )
|
|
{
|
|
n_result = [[ level._margwa_damage_cb ]]( inflictor, attacker, damage, dFlags, mod, weapon, point, dir, hitLoc, offsetTime, boneIndex, modelIndex );
|
|
|
|
if( isdefined( n_result ) )
|
|
{
|
|
return n_result;
|
|
}
|
|
}
|
|
|
|
damageOpen = false; // mouth was open during damage
|
|
|
|
if ( !( isdefined( self.canDamage ) && self.canDamage ) )
|
|
{
|
|
self.health += 1; // impact fx only work when damage is applied
|
|
return 1;
|
|
}
|
|
|
|
if ( isDirectHitWeapon( weapon ) )
|
|
{
|
|
headAlive = [];
|
|
foreach ( head in self.head )
|
|
{
|
|
if ( head margwaCanDamageHead() )
|
|
{
|
|
headAlive[ headAlive.size ] = head;
|
|
}
|
|
}
|
|
|
|
if ( headAlive.size > 0 )
|
|
{
|
|
max = 100000;
|
|
headClosest = undefined;
|
|
foreach ( head in headAlive )
|
|
{
|
|
distSq = DistanceSquared( point, self GetTagOrigin( head.tag ) );
|
|
if ( distSq < max )
|
|
{
|
|
max = distSq;
|
|
headClosest = head;
|
|
}
|
|
}
|
|
if ( IsDefined( headClosest ) )
|
|
{
|
|
if ( max < 24 * 24 )
|
|
{
|
|
if ( isdefined( level.margwa_damage_override_callback ) && IsFunctionPtr( level.margwa_damage_override_callback ) )
|
|
{
|
|
damage = attacker [[ level.margwa_damage_override_callback ]]( damage );
|
|
}
|
|
|
|
headClosest.health -= damage;
|
|
damageOpen = true;
|
|
self clientfield::increment( headClosest.impactCF );
|
|
attacker show_hit_marker();
|
|
|
|
if ( headClosest.health <= 0 )
|
|
{
|
|
if( isdefined(level.margwa_head_kill_weapon_check) )
|
|
{
|
|
[[level.margwa_head_kill_weapon_check]]( self, weapon );
|
|
}
|
|
|
|
if ( self margwaKillHead( headClosest.model, attacker ) )
|
|
{
|
|
return self.health;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
partName = GetPartName( self.model, boneIndex );
|
|
if ( IsDefined( partName ) )
|
|
{
|
|
/#
|
|
if ( ( isdefined( self.debugHitLoc ) && self.debugHitLoc ) )
|
|
{
|
|
PrintTopRightLn( partName + " damage: " + damage );
|
|
}
|
|
#/
|
|
modelHit = self margwaHeadHit( self, partName );
|
|
if ( IsDefined( modelHit ) )
|
|
{
|
|
headInfo = self.head[ modelHit ];
|
|
if ( headInfo margwaCanDamageHead() )
|
|
{
|
|
if ( isdefined( level.margwa_damage_override_callback ) && IsFunctionPtr( level.margwa_damage_override_callback ) )
|
|
{
|
|
damage = attacker [[ level.margwa_damage_override_callback ]]( damage );
|
|
}
|
|
|
|
if( isdefined( attacker ) )
|
|
{
|
|
attacker notify( "margwa_headshot", self );
|
|
}
|
|
|
|
headInfo.health -= damage;
|
|
damageOpen = true;
|
|
self clientfield::increment( headInfo.impactCF );
|
|
attacker show_hit_marker();
|
|
|
|
if ( headInfo.health <= 0 )
|
|
{
|
|
if( isdefined(level.margwa_head_kill_weapon_check) )
|
|
{
|
|
[[level.margwa_head_kill_weapon_check]]( self, weapon );
|
|
}
|
|
|
|
if ( self margwaKillHead( modelHit, attacker ) )
|
|
{
|
|
return self.health;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( damageOpen )
|
|
{
|
|
return 0; // custom fx when damaging head
|
|
}
|
|
|
|
self.health += 1; // impact fx only work when damage is applied to ent
|
|
return 1;
|
|
}
|
|
|
|
function private margwaHeadHit( entity, partName )
|
|
{
|
|
switch ( partName )
|
|
{
|
|
case "j_chunk_head_bone_le":
|
|
case "j_jaw_lower_1_le":
|
|
return self.head_left_model;
|
|
|
|
case "j_chunk_head_bone":
|
|
case "j_jaw_lower_1":
|
|
return self.head_mid_model;
|
|
|
|
case "j_chunk_head_bone_ri":
|
|
case "j_jaw_lower_1_ri":
|
|
return self.head_right_model;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function private margwaUpdateMoveSpeed()
|
|
{
|
|
if ( self.zombie_move_speed == "walk" )
|
|
{
|
|
self.zombie_move_speed = "run";
|
|
//self ASMSetAnimationRate( 0.8 );
|
|
Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_run" );
|
|
}
|
|
else if ( self.zombie_move_speed == "run" )
|
|
{
|
|
self.zombie_move_speed = "sprint";
|
|
//self ASMSetAnimationRate( 1.0 );
|
|
Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_sprint" );
|
|
}
|
|
}
|
|
|
|
function margwaForceSprint()
|
|
{
|
|
self.zombie_move_speed = "sprint";
|
|
Blackboard::SetBlackBoardAttribute( self, "_locomotion_speed", "locomotion_speed_sprint" );
|
|
}
|
|
|
|
function private margwaDestroyHead( modelHit )
|
|
{
|
|
}
|
|
|
|
function shouldUpdateJaw()
|
|
{
|
|
if ( !( isdefined( self.jawAnimEnabled ) && self.jawAnimEnabled ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( self.headAttached < 3 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function margwaSetGoal( origin, radius, boundaryDist )
|
|
{
|
|
pos = GetClosestPointOnNavMesh( origin, 64, 30 );
|
|
if ( IsDefined( pos ) )
|
|
{
|
|
self SetGoal( pos );
|
|
return true;
|
|
}
|
|
|
|
self SetGoal( self.origin );
|
|
return false;
|
|
}
|
|
|
|
function private margwaWait()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self.waiting = true;
|
|
self.needTeleportIn = true;
|
|
|
|
destPos = self.teleportPos + ( 0, 0, 60 );
|
|
dist = Distance( self.teleportStart, destPos );
|
|
time = dist / 600;
|
|
|
|
if ( isdefined( self.margwa_teleport_speed ) )
|
|
{
|
|
if ( self.margwa_teleport_speed > 0 )
|
|
{
|
|
time = dist / self.margwa_teleport_speed;
|
|
}
|
|
}
|
|
|
|
if ( isdefined( self.traveler ) )
|
|
{
|
|
self thread margwaTell();
|
|
|
|
self.traveler MoveTo( destPos, time );
|
|
self.traveler util::waittill_any_ex( ( time + 0.1 ), "movedone", self, "death" );
|
|
|
|
self.travelerTell clientfield::set( "margwa_fx_travel_tell", 0 );
|
|
}
|
|
|
|
self.waiting = false;
|
|
self.needTeleportOut = false;
|
|
|
|
if ( isdefined( self.margwa_teleport_speed ) )
|
|
{
|
|
self.margwa_teleport_speed = undefined;
|
|
}
|
|
}
|
|
|
|
function margwaTell()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self.travelerTell.origin = self.teleportPos;
|
|
|
|
util::wait_network_frame();
|
|
|
|
self.travelerTell clientfield::set( "margwa_fx_travel_tell", 1 );
|
|
}
|
|
|
|
function private shieldFacing( vDir, limit, front = true )
|
|
{
|
|
orientation = self getPlayerAngles();
|
|
forwardVec = anglesToForward( orientation );
|
|
if ( !front )
|
|
{
|
|
forwardVec = -forwardVec;
|
|
}
|
|
forwardVec2D = ( forwardVec[0], forwardVec[1], 0 );
|
|
unitForwardVec2D = VectorNormalize( forwardVec2D );
|
|
|
|
toFaceeVec = -vDir;
|
|
toFaceeVec2D = ( toFaceeVec[0], toFaceeVec[1], 0 );
|
|
unitToFaceeVec2D = VectorNormalize( toFaceeVec2D );
|
|
|
|
dotProduct = VectorDot( unitForwardVec2D, unitToFaceeVec2D );
|
|
return ( dotProduct > limit ); // more or less in front
|
|
}
|
|
|
|
function private inSmashAttackRange( enemy )
|
|
{
|
|
smashPos = self.origin;
|
|
|
|
heightOffset = abs( self.origin[2] - enemy.origin[2] );
|
|
if ( heightOffset > 48 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
distSq = DistanceSquared( smashPos, enemy.origin );
|
|
range = 160 * 160;
|
|
|
|
if ( distSq < range )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|