IL-GSC/BO1/PC/ZM/animscripts/zombie_melee.gsc
2024-02-18 17:32:07 -05:00

502 lines
13 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include maps\_utility;
#include animscripts\zombie_utility;
#include animscripts\zombie_SetPoseMovement;
#include animscripts\Combat_Utility;
#include animscripts\Debug;
#include common_scripts\utility;
#using_animtree ("generic_human");
MeleeCombat()
{
self endon( "end_melee" );
self endon("killanimscript");
self melee_notify_wrapper();
assert( CanMeleeAnyRange() );
if(IsDefined(level.allow_ai_vs_ai_melee ))
{
doingAIMelee = (isAI( self.enemy ) && self.enemy.type == "human");
}
else
{
doingAIMelee = false;
}
if ( doingAiMelee )
{
assert( animscripts\zombie_utility::okToMelee( self.enemy ) );
animscripts\zombie_utility::IAmMeleeing( self.enemy );
AiVsAiMeleeCombat();
animscripts\zombie_utility::ImNotMeleeing( self.enemy );
scriptChange();
return;
}
realMelee = true;
if ( animscripts\zombie_utility::okToMelee(self.enemy) )
{
animscripts\zombie_utility::IAmMeleeing(self.enemy);
}
else
{
realMelee = false;
}
self thread EyesAtEnemy();
self OrientMode("face enemy");
MeleeDebugPrint("Melee begin");
self AnimMode( "zonly_physics" );
resetGiveUpTime();
for ( ;; )
{
if ( IsDefined(self.marked_for_death) )
{
return;
}
MeleeDebugPrint("Melee main loop" + RandomInt(100));
if ( !realMelee && animscripts\zombie_utility::okToMelee(self.enemy) )
{
realMelee = true;
animscripts\zombie_utility::IAmMeleeing(self.enemy);
}
self thread EyesAtEnemy();
self animscripts\battleChatter_ai::evaluateMeleeEvent();
if( IsDefined( self.enemy ) )
{
angles = VectorToAngles( self.enemy.origin - self.origin );
self OrientMode( "face angle", angles[1] );
}
if( !IsDefined(level.zombietron_mode) )
{
switch( self.animname )
{
case "zombie":
self PlaySound( "zmb_vocals_zombie_attack" );
break;
case "quad_zombie":
self PlaySound( "zmb_vocals_quad_attack" );
break;
case "boss_zombie":
self PlaySound( "zmb_vocals_boss_attack" );
break;
case "napalm_zombie":
self PlaySound( "zmb_vocals_napalm_attack" );
break;
case "sonic_zombie":
self PlaySound( "zmb_vocals_sonic_attack" );
break;
}
}
if ( is_true( self.noChangeDuringMelee ) )
{
self.safeToChangeScript = false;
}
zombie_attack = pick_zombie_melee_anim( self );
if ( isDefined( self.melee_anim_func ) )
{
self thread [[ self.melee_anim_func ]]( zombie_attack );
}
self SetFlaggedAnimKnobAllRestart("meleeanim", zombie_attack, %body, 1, .2, 1);
while ( 1 )
{
self waittill("meleeanim", note);
if ( note == "end" )
{
break;
}
else if ( note == "fire" )
{
if ( !IsDefined( self.enemy ) )
{
break;
}
oldhealth = self.enemy.health;
self melee();
if ( self.enemy.health < oldhealth )
{
resetGiveUpTime();
}
else
{
if ( isDefined( self.melee_miss_func ) )
{
self [[ self.melee_miss_func ]]();
}
else if ( isDefined( level.melee_miss_func ) )
{
self [[ level.melee_miss_func ]]();
}
}
}
else if ( note == "stop" )
{
if ( !CanContinueToMelee() )
{
break;
}
}
}
self OrientMode("face default");
if ( is_true( self.noChangeDuringMelee ) )
{
if ( isDefined( self.enemy ) )
{
dist_sq = DistanceSquared( self.origin, self.enemy.origin );
if ( dist_sq > self.meleeAttackDist * self.meleeAttackDist )
{
self.safeToChangeScript = true;
wait_network_frame();
break;
}
}
else
{
self.safeToChangeScript = true;
wait_network_frame();
break;
}
}
}
if (realMelee)
{
animscripts\zombie_utility::ImNotMeleeing(self.enemy);
}
self AnimMode("none");
self thread animscripts\zombie_combat::main();
self notify ("stop EyesAtEnemy");
self notify ("stop_melee_debug_print");
scriptChange();
}
resetGiveUpTime()
{
if ( DistanceSquared( self.origin, self.enemy.origin ) > anim.chargeRangeSq )
{
self.giveUpOnMeleeTime = GetTime() + randomintrange( 2700, 3300 );
}
else
{
self.giveUpOnMeleeTime = GetTime() + randomintrange( 1700, 2300 );
}
}
MeleeDebugPrint(text)
{
return;
self.meleedebugprint = text;
self thread meleeDebugPrintThreadWrapper();
}
meleeDebugPrintThreadWrapper()
{
if ( !IsDefined(self.meleedebugthread) )
{
self.meleedebugthread = true;
self meleeDebugPrintThread();
self.meleedebugthread = undefined;
}
}
meleeDebugPrintThread()
{
self endon("death");
self endon("killanimscript");
self endon("stop_melee_debug_print");
while(1)
{
Print3d(self.origin + (0,0,60), self.meleedebugprint, (1,1,1), 1, .1);
wait .05;
}
}
debug_melee_on_actor()
{
return false;
}
debug_melee( msg )
{
if ( !debug_melee_on_actor() )
{
return;
}
PrintLn( msg );
}
debug_melee_line( start, end, color, duration )
{
}
CanContinueToMelee()
{
return CanMeleeInternal( "already started" );
}
CanMeleeAnyRange()
{
return CanMeleeInternal( "any range" );
}
CanMeleeDesperate()
{
return CanMeleeInternal( "long range" );
}
CanMelee()
{
return CanMeleeInternal( "normal" );
}
CanMeleeInternal( state )
{
if ( !IsSentient( self.enemy ) )
{
debug_melee( "Not doing melee - Does not have a valid target." );
return false;
}
if (!IsAlive(self.enemy))
{
return false;
}
if ( IsDefined( self.disableMelee ) )
{
assert( self.disableMelee );
debug_melee( "Not doing melee - Melee is disabled, self.disableMelee is set to true." );
return false;
}
yaw = abs(getYawToEnemy());
if ( (yaw > 60 && state != "already started") || yaw > 110 )
{
debug_melee( "Not doing melee - Not facing the enemy." );
return false;
}
enemyPoint = self.enemy GetOrigin();
vecToEnemy = enemyPoint - self.origin;
self.enemyDistanceSq = lengthSquared( vecToEnemy );
nearest_enemy_sqrd_dist = self GetClosestEnemySqDist();
epsilon = 0.1;
if( IsDefined( nearest_enemy_sqrd_dist ) && ( nearest_enemy_sqrd_dist - epsilon > self.enemyDistanceSq ) )
{
debug_melee( "Not doing melee - Entity " + self getEntityNumber() + " can't melee entity " + self.enemy getEntityNumber() + " at distSq " + self.enemyDistanceSq + " because there is a closer enemy at distSq " + nearest_enemy_sqrd_dist + "." );
return false;
}
if(IsDefined(level.allow_ai_vs_ai_melee ))
{
doingAIMelee = (isAI( self.enemy ) && self.enemy.type == "human");
}
else
{
doingAIMelee = false;
}
if ( doingAIMelee )
{
if ( !animscripts\zombie_utility::okToMelee(self.enemy) )
{
debug_melee( "Not doing melee - Enemy is already being meleed." );
return false;
}
if ( IsDefined( self.magic_bullet_shield ) && self.magic_bullet_shield && IsDefined( self.enemy.magic_bullet_shield ) && self.enemy.magic_bullet_shield )
{
debug_melee( "Not doing melee - Enemy has magic bullet shield." );
return false;
}
if ( !isMeleePathClear( vecToEnemy, enemyPoint ) )
{
return false;
}
}
else
{
if ( IsDefined( self.enemy.magic_bullet_shield ) && self.enemy.magic_bullet_shield )
{
if ( !( self is_banzai() ) )
{
debug_melee( "Not doing melee - Enemy has magic bullet shield." );
return false;
}
}
if (self.enemyDistanceSq <= anim.meleeRangeSq)
{
if ( !isMeleePathClear( vecToEnemy, enemyPoint ) )
{
if ( !self is_banzai() )
{
return false;
}
}
return true;
}
else if ( self is_banzai() )
{
return false;
}
if ( state != "any range" )
{
chargeRangeSq = anim.chargeRangeSq;
if ( state == "long range" )
{
chargeRangeSq = anim.chargeLongRangeSq;
}
if (self.enemyDistanceSq > chargeRangeSq)
{
debug_melee( "Not doing melee - Enemy is not close enough to charge." );
return false;
}
}
if ( state == "already started" )
{
return false;
}
if ( ( !self is_banzai() || IsPlayer( self.enemy ) ) && IsDefined( self.lastMeleeGiveUpTime ) && GetTime() - self.lastMeleeGiveUpTime < 3000 )
{
debug_melee( "Not doing melee - Recently meleed someone and missed." );
return false;
}
if ( !animscripts\zombie_utility::okToMelee(self.enemy) )
{
debug_melee( "Not doing melee - Enemy is being meleed." );
return false;
}
if ( is_true( self.check_melee_path ) )
{
if( !isMeleePathClear( vecToEnemy, enemyPoint ) )
{
self notify("melee_path_blocked");
return false;
}
}
}
return true;
}
isMeleePathClear( vecToEnemy, enemyPoint )
{
dirToEnemy = VectorNormalize( (vecToEnemy[0], vecToEnemy[1], 0 ) );
meleePoint = enemyPoint - ( dirToEnemy[0]*28, dirToEnemy[1]*28, 0 );
thread debug_melee_line( self.origin, meleePoint, ( 1,0,0 ), 1.5 );
if ( !self IsInGoal( meleePoint ) )
{
debug_melee( "Not doing melee - Enemy is outside not in goal." );
return false;
}
if ( self maymovetopoint(meleePoint) )
{
return true;
}
trace1 = bullettrace( self.origin + (0,0,20), meleePoint + (0,0,20), true, self );
trace2 = bullettrace( self.origin + (0,0,72), meleePoint + (0,0,72), true, self );
if ( isDefined(trace1["fraction"]) && trace1["fraction"] == 1 &&
isDefined(trace2["fraction"]) && trace2["fraction"] == 1 )
{
return true;
}
if ( isDefined(trace1["entity"]) && trace1["entity"] == self.enemy &&
isDefined(trace2["entity"]) && trace2["entity"] == self.enemy )
{
return true;
}
if ( is_true( level.zombie_melee_in_water ) )
{
if ( isDefined( trace1["surfacetype"] ) && trace1["surfacetype"] == "water" &&
isDefined( trace2["fraction"] ) && trace2["fraction"] == 1 )
{
return true;
}
}
debug_melee( "Not doing melee - Can not move to the melee point, MayMoveToPoint failed." );
return false;
}
PlayMeleeSound()
{
if ( !IsDefined ( self.a.nextMeleeChargeSound ) )
{
self.a.nextMeleeChargeSound = 0;
}
if ( GetTime() > self.a.nextMeleeChargeSound )
{
self animscripts\face::SaySpecificDialogue( undefined, "chr_play_grunt_" + self.voice, 0.3 );
self.a.nextMeleeChargeSound = GetTime() + 8000;
}
}
AiVsAiMeleeCombat()
{
self endon("killanimscript");
self melee_notify_wrapper();
self OrientMode("face enemy");
self ClearAnim( %root, 0.3 );
IWin = ( RandomInt(10) < 8 );
if ( IsDefined( self.magic_bullet_shield ) && self.magic_bullet_shield )
{
IWin = true;
}
if ( IsDefined( self.enemy.magic_bullet_shield ) && self.enemy.magic_bullet_shield )
{
IWin = false;
}
winAnim = %bog_melee_R_attack;
loseAnim = %bog_melee_R_defend;
if ( IWin )
{
myAnim = winAnim;
theirAnim = loseAnim;
}
else
{
myAnim = loseAnim;
theirAnim = winAnim;
}
desiredDistSqrd = 72 * 72;
self PlayMeleeSound();
AiVsAiMeleeCharge( desiredDistSqrd );
if ( DistanceSquared( self.origin, self.enemy.origin ) > desiredDistSqrd )
{
return false;
}
self.meleePartner = self.enemy;
self.enemy.meleePartner = self;
self.enemy.meleeAnim = theirAnim;
self.enemy animcustom( ::AiVsAiAnimCustom );
self.meleeAnim = myAnim;
self animcustom( ::AiVsAiAnimCustom );
}
AiVsAiMeleeCharge( desiredDistSqrd )
{
giveUpTime = GetTime() + 2500;
self SetAnimKnobAll( animscripts\zombie_run::GetRunAnim(), %body, 1, 0.2 );
while ( DistanceSquared( self.origin, self.enemy.origin ) > desiredDistSqrd && GetTime() < giveUpTime )
{
wait .05;
}
}
AiVsAiAnimCustom()
{
self endon("killanimscript");
self AiVsAiMeleeAnim( self.meleeAnim );
}
AiVsAiMeleeAnim( myAnim )
{
self endon("end_melee");
self thread endMeleeOnKillanimscript();
partnerDir = self.meleePartner.origin - self.origin;
self OrientMode( "face angle", VectorToAngles( partnerDir )[1] );
self AnimMode( "zonly_physics" );
self SetFlaggedAnimKnobAllRestart( "meleeAnim", myAnim, %body, 1, 0.2 );
self animscripts\zombie_shared::DoNoteTracks( "meleeAnim" );
self notify("end_melee");
}
endMeleeOnKillanimscript()
{
self endon("end_melee");
self waittill("killanimscript");
self.meleePartner notify("end_melee");
}
pick_zombie_melee_anim( zombie_guy )
{
melee_anim = undefined;
if ( zombie_guy.has_legs )
{
switch(zombie_guy.zombie_move_speed)
{
case "walk":
anims = array_combine(level._zombie_melee[zombie_guy.animname],level._zombie_walk_melee[zombie_guy.animname]);
melee_anim = random(anims);
break;
case "run":
case "sprint":
anims = array_combine(level._zombie_melee[zombie_guy.animname],level._zombie_run_melee[zombie_guy.animname]);
melee_anim = random(anims);
break;
}
}
else if(zombie_guy.a.gib_ref == "no_legs")
{
melee_anim = random(level._zombie_stumpy_melee[zombie_guy.animname]);
}
else
{
melee_anim = random(level._zombie_melee_crawl[zombie_guy.animname]);
}
return melee_anim;
}