boiii-scripts/shared/ai/margwa.gsc
2023-04-13 17:30:38 +02:00

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;
}