rott/kf_sources/SideshowScript/Classes/KF_RingMasterNPC.uc
2026-07-14 20:27:09 +07:00

399 lines
11 KiB
Ucode

/*
--------------------------------------------------------------
KF_RingMasterNPC
--------------------------------------------------------------
A helpeless old man.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_RingMasterNPC extends KF_StoryNPC_Spawnable;
/* Achievement Specific Variable */
var int TimesHit; // Increments each time the ring master is hit
var int MaxTimesHit; // Number of hits it takes to fail a ringmaster achievement
var bool bFailedAchievement; // The ring master will never get this achievement... he failed...
/* Anim to play when this NPC is scared / taking damage */
var name CowerAnim;
/* Anim to play when this NPC is seated on the ground & inactive */
var name IdleSeatedAnim;
/* Anim to play when this NPC is idle and standing */
var name IdleStandingAnim;
/* Anim to play when this NPC is idle and standing but is very hurt */
var name IdleStandingInjuredAnim;
/* Anim to play when this NPC gets up off the ground */
var name StandUpAnim;
/* If true, play standup anim */
var bool bFirstActivation;
var float LastCowerTime;
/* An array of sounds specifically for bleeding */
var() Array<Sound> BleedingSounds;
var float MinTimeBetweenBleedSounds;
var float LastBleedSoundTime;
var float LastBleedOutTime, BleedOutInterval;
replication
{
reliable if (Role == Role_Authority)
bFirstActivation;
}
simulated function PostBeginPlay()
{
Super.PostBeginPlay();
log("*** SPAWNED RINGMASTER *** "@self);
// Scale bleed out by difficulty if you want to
if ( Role == ROLE_Authority && Level.Game != none )
{
// Set difficulty based values
if ( Level.Game.GameDifficulty >= 7.0 ) // Hell on Earth
{
BleedOutInterval *= 0.5;
}
else if ( Level.Game.GameDifficulty >= 5.0 ) // Suicidal
{
BleedOutInterval *= 0.5;
}
else if ( Level.Game.GameDifficulty >= 4.0 ) // Hard
{
BleedOutInterval *= 0.5;
}
else if ( Level.Game.GameDifficulty >= 2.0 ) // Normal
{
BleedOutInterval *= 0.75;
}
else //if ( GameDifficulty == 1.0 ) // Beginner
{
BleedOutInterval *= 1.0;
}
// Playercount Scaling. Make the guy bleed slower if there are fewer player
if( Level.Game.NumPlayers == 1 )
{
BleedOutInterval *= 1.5;
}
else if( Level.Game.NumPlayers <= 3 )
{
BleedOutInterval *= 1.25;
}
}
}
function Reset()
{
Super.Reset();
bFirstActivation = false;
}
simulated function Tick(float DeltaTime)
{
// log(self@" Active ? ::: "@bActive);
if(bActive && Level.TimeSeconds - LastBleedOutTime > BleedOutInterval)
{
LastBleedOutTime = Level.TimeSeconds;
TakeDamage(5,self,Location + Vect(0,0,1)*CollisionHeight,vect(0,0,0),class 'DamTypeBleedOut');
}
Super.Tick(DeltaTime);
/* Update his idle state to reflect his injuries , etc. */
IdleRestAnim = GetIdleAnim();
IdleWeaponAnim = IdleRestAnim;
}
function PlayTakeHit(vector HitLocation, int Damage, class<DamageType> DamageType)
{
// Do not play the ringmasters pain sound from bleeding damage
if ( class<DamTypeBleedOut>( damageType ) == none )
{
Super.PlayTakeHit( HitLocation, Damage, DamageType );
return;
}
if( Level.TimeSeconds - LastBleedSoundTime < MinTimeBetweenBleedSounds )
return;
LastBleedSoundTime = Level.TimeSeconds;
// the ringmaster will play his specific bleeding sound
PlaySound( BleedingSounds[ Rand ( BleedingSounds.Length ) ],,1.75 );
}
simulated function AddHealth()
{
local int tempHeal ;
if((level.TimeSeconds - lastHealTime) >= 0.1)
{
//log("AddHealth HealthToGive = "$HealthToGive$" Health = "$Health);
if(Health < HealthMax)
{
tempHeal = int(20 * (level.TimeSeconds - lastHealTime)) ;
if(tempHeal>0)
lastHealTime = level.TimeSeconds ;
//log("AddHealth adding "$tempHeal$" health");
Health = Min(Health+tempHeal, HealthMax);
HealthToGive -= tempHeal ;
}
else
{
lastHealTime = level.timeSeconds ;
// if we are all healed, there's gonna be no more healing
HealthToGive = 0 ;
log("AddHealth all healed Health = "$Health);
}
}
}
function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, optional int HitIdx )
{
// Reduce damage for zombie attacks
if( (class<DamTypeZombieAttack>(damageType) != none || class<DamTypeBurned>(damageType) != none
|| class<DamTypeLawRocketImpact>(damageType) != none) && Damage >= 4 )
{
Damage *= 0.25;
}
// Cancel out the 5 we are going to lose when TakeDamage is called in the super
// This is done so that healing works better for the Ringmaster, as his constant
// bleedout is making healing not work well
if( damageType == class'DamTypeBleedOut' && healthToGive > 0 )
{
healthtoGive+=5;
}
//log("Taking "$Damage$" damage of type "$damageType);
// pushing the Ringmaster off his path can be problematic, so zero the momentum
super.TakeDamage( Damage, instigatedBy, hitlocation, vect(0,0,0), damageType, HitIdx );
if ( InstigatedBy != none && !InstigatedBy.IsHumanControlled() )
{
CheckObjectiveAchievements( damageType );
}
}
function CheckObjectiveAchievements( class<DamageType> damageType )
{
local KFSteamStatsAndAchievements KFSteamStats;
local Controller C;
local KFGameReplicationInfo KFGRI;
if ( class<DamTypeBleedOut>( damageType ) == none )
{
TimesHit += 1;
if ( TimesHit >= MaxTimesHit && !bFailedAchievement )
{
bFailedAchievement = true;
KFGRI = KFGameReplicationInfo( Level.GRI );
if( KFGRI != none )
{
KFGRI.bObjectiveAchievementFailed = true;
}
}
}
}
function OnObjectiveChanged(name OldObjectiveName, name NewObjectiveName)
{
if ( NewObjectiveName == 'DefendRingMaster' )
{
TimesHit = 0;
bFailedAchievement = false;
}
}
/* Wrapper for retrieving the type of idle animation the Ringmaster should play at the moment */
simulated function name GetIdleAnim()
{
if(!bActive && !bFirstActivation)
{
return IdleSeatedAnim;
}
return IdleStandingInjuredAnim;
}
simulated event SetAnimAction(name NewAction)
{
if (!bWaitForAnim)
{
AnimAction = NewAction;
if ( AnimAction == StandUpAnim )
{
bWaitForAnim = true;
AnimBlendParams(0, 1.0, 0.0, 0.2, FireRootBone);
PlayAnim(AnimAction,1.f, 0.0, 1);
}
else if(NewAction == CowerAnim )
{
AnimBlendParams(1, 1.0, 0.0, 0.2, FireRootBone);
PlayAnim(AnimAction,1.f, 0.0, 1);
}
}
super.SetAnimAction(NewAction);
}
simulated Function PostNetBeginPlay()
{
local PlayerController LocalPlayer;
local HUD_Storymode MyStoryHUD;
local int i;
super.PostNetBeginPlay();
/* MEGA HACK!!!!!!!! *================================================*/
LocalPlayer = Level.GetLocalPlayerController();
if(LocalPlayer != none)
{
MyStoryHUD = HUD_StoryMode(LocalPlayer.myHUD) ;
if(MyStoryHUD != none)
{
for( i = 0; i < MyStoryHUD.ConditionHints.length; i++)
{
if(MyStoryHUD.ConditionHints[i].ConditionLoc == none &&
MyStoryHUD.ConditionHints[i].PendingLocActorTag != '')
{
MyStoryHUD.ConditionHints[i].ConditionLoc = self;
MyStoryHUD.ConditionHints[i].PendingLocActorTag = '' ;
}
}
}
}
}
/* Only do the arm flailing animation if a ZED is attacking */
simulated function PlayDirectionalHit(Vector HitLoc)
{
if(LasthitBy != none &&
LastHitBy.IsA('KFMonsterController') &&
Level.TimeSeconds - LastCowerTime >= GetAnimDuration(CowerAnim,1.f))
{
LastCowerTime = Level.TimeSeconds;
SetAnimAction(CowerAnim);
}
}
function SetActive(bool On)
{
Super.SetActive(On);
if(bActive && !bFirstActivation)
{
bFirstActivation = true;
}
}
event Bump( actor Other )
{
local vector PushVelocity;
if ( Velocity != vect(0,0,0) )
{
// cheap and dirty encroachment
if ( Other.Physics == PHYS_Walking && KFHumanPawn(Other) != None )
{
//log( self$" BUMPED "$Other );
if( normal(Velocity) dot normal(Other.Location - Location) >= 0.7 )
{
PushVelocity = Normal( Other.Location - Location ) * 150;
PushVelocity.Z = 50;
Pawn( Other ).AddVelocity( PushVelocity );
}
}
}
}
// copied from Pawn.uc, but we don't want the ringmaster to freeze on match end
function TurnOff()
{
//SetCollision(true,false);
AmbientSound = None;
bNoWeaponFiring = true;
//Velocity = vect(0,0,0);
//SetPhysics(PHYS_None);
//bPhysicsAnimUpdate = false;
bIsIdle = true;
bWaitForAnim = false;
StopAnimating();
//bIgnoreForces = true;
}
defaultproperties
{
//NotThreateningTo(0)= class 'ZombieFleshPound'
//NotThreateningTo(1)= class 'ZombieScrake'
BaseAIThreatRating = 0.01f
MaxTimesHit=15
bShowHealthBar = true
NPCHealth = 250
StartingHealthPct = 0.5
FriendlyFireDamageScale=0.f
bDropInventoryOnDeath = false
BleedOutInterval = 1
NPCName = "Ringmaster Lockhart"
HealedEvent="RingMasterHealed"
bStartActive = false
AIScriptTag=RingMasterAI
/* Animation */
bNoDefaultInventory = true
bCanPickupInventory = false
//GroundSpeed = 110
GroundSpeed=98
Mesh = SkeletalMesh'KF_Ringmaster_Trip.Ringmaster'
CowerAnim = "Freaking_out"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkB"
MovementAnims(2)="WalkL"
MovementAnims(3)="WalkR"
HitAnims(0)="Freaking_out"
HitAnims(1)="Freaking_out"
HitAnims(2)="Freaking_out"
HitAnims(3)="Freaking_out"
IdleSeatedAnim="Heal_Sitting"
IdleChatAnim="Idle_Talk"
IdleStandingInjuredAnim="Idle_VeryWounded"
IdleStandingAnim="Idle_Talk"
StandUpAnim="Heal_Getup"
MinTimeBetweenPainSounds=1
MinTimeBetweenBleedSounds=5.0
bAlwaysRelevant=true
BleedingSounds(0)=Sound'SummerBoardwalkDialogue.DE_260_1'
BleedingSounds(1)=Sound'SummerBoardwalkDialogue.DE_270_Grunt_02'
BleedingSounds(2)=Sound'SummerBoardwalkDialogue.DE_270_Grunt_03'
BleedingSounds(3)=Sound'SummerBoardwalkDialogue.DE_270_Knee_01'
BleedingSounds(4)=Sound'SummerBoardwalkDialogue.DE_280_4'
}