Fix new line symbol issues
This commit is contained in:
parent
51bb9add5b
commit
d66d86b2b1
846 changed files with 33896 additions and 12983 deletions
|
|
@ -1,33 +1,60 @@
|
|||
class MeanHuskFireProjectile extends NiceHuskFireProjectile;
|
||||
simulated singular function Touch(Actor Other){
|
||||
local vector HitLocation, HitNormal;
|
||||
//Don't touch bulletwhip attachment. Taken from HuskFireProjectile
|
||||
if ( Other == none || KFBulletWhipAttachment(Other) != none )
return;
|
||||
if ( Other.bProjTarget || Other.bBlockActors ) {
LastTouched = Other;
if ( Velocity == vect(0,0,0) || Other.IsA('Mover') ) {
ProcessTouch(Other,Location);
LastTouched = none;
return;
}
|
||||
if ( Other.TraceThisActor(HitLocation, HitNormal, Location, Location - 2*Velocity, GetCollisionExtent()) )
HitLocation = Location;
|
||||
ProcessTouch(Other, HitLocation);
LastTouched = none;
if ( (Role < ROLE_Authority) && (Other.Role == ROLE_Authority) && (Pawn(Other) != none) )
ClientSideTouch(Other, HitLocation);
|
||||
}
|
||||
}
|
||||
// Don't hit Zed extra collision cylinders
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation) {
|
||||
// Don't let it hit this player, or blow up on another player
|
||||
if (Other == none || Other == Instigator || Other.Base == Instigator)
return;
|
||||
// Don't collide with bullet whip attachments
|
||||
if (KFBulletWhipAttachment(Other) != none) {
return;
|
||||
}
|
||||
// Use the instigator's location if it exists. This fixes issues with
|
||||
// the original location of the projectile being really far away from
|
||||
// the real Origloc due to it taking a couple of milliseconds to
|
||||
// replicate the location to the client and the first replicated location has
|
||||
// already moved quite a bit.
|
||||
if (Instigator != none) {
OrigLoc = Instigator.Location;
|
||||
}
|
||||
if (!bDud && ((VSizeSquared(Location - OrigLoc) < ArmDistSquared) || OrigLoc == vect(0,0,0))) {
if( Role == ROLE_Authority ) {
AmbientSound=none;
PlaySound(Sound'ProjectileSounds.PTRD_deflect04',,2.0);
Other.TakeDamage( ImpactDamage, Instigator, HitLocation, Normal(Velocity), ImpactDamageType );
}
|
||||
bDud = true;
Velocity = vect(0,0,0);
LifeSpan=1.0;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
if (!bDud) {
Explode(HitLocation,Normal(HitLocation-Other.Location));
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
additionalDamagePart=0.250000
|
||||
}
|
||||
class MeanHuskFireProjectile extends NiceHuskFireProjectile;
|
||||
simulated singular function Touch(Actor Other){
|
||||
local vector HitLocation, HitNormal;
|
||||
//Don't touch bulletwhip attachment. Taken from HuskFireProjectile
|
||||
if ( Other == none || KFBulletWhipAttachment(Other) != none )
|
||||
return;
|
||||
if ( Other.bProjTarget || Other.bBlockActors ) {
|
||||
LastTouched = Other;
|
||||
if ( Velocity == vect(0,0,0) || Other.IsA('Mover') ) {
|
||||
ProcessTouch(Other,Location);
|
||||
LastTouched = none;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Other.TraceThisActor(HitLocation, HitNormal, Location, Location - 2*Velocity, GetCollisionExtent()) )
|
||||
HitLocation = Location;
|
||||
|
||||
ProcessTouch(Other, HitLocation);
|
||||
LastTouched = none;
|
||||
if ( (Role < ROLE_Authority) && (Other.Role == ROLE_Authority) && (Pawn(Other) != none) )
|
||||
ClientSideTouch(Other, HitLocation);
|
||||
}
|
||||
}
|
||||
// Don't hit Zed extra collision cylinders
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation) {
|
||||
// Don't let it hit this player, or blow up on another player
|
||||
if (Other == none || Other == Instigator || Other.Base == Instigator)
|
||||
return;
|
||||
// Don't collide with bullet whip attachments
|
||||
if (KFBulletWhipAttachment(Other) != none) {
|
||||
return;
|
||||
}
|
||||
// Use the instigator's location if it exists. This fixes issues with
|
||||
// the original location of the projectile being really far away from
|
||||
// the real Origloc due to it taking a couple of milliseconds to
|
||||
// replicate the location to the client and the first replicated location has
|
||||
// already moved quite a bit.
|
||||
if (Instigator != none) {
|
||||
OrigLoc = Instigator.Location;
|
||||
}
|
||||
if (!bDud && ((VSizeSquared(Location - OrigLoc) < ArmDistSquared) || OrigLoc == vect(0,0,0))) {
|
||||
if( Role == ROLE_Authority ) {
|
||||
AmbientSound=none;
|
||||
PlaySound(Sound'ProjectileSounds.PTRD_deflect04',,2.0);
|
||||
Other.TakeDamage( ImpactDamage, Instigator, HitLocation, Normal(Velocity), ImpactDamageType );
|
||||
}
|
||||
|
||||
bDud = true;
|
||||
Velocity = vect(0,0,0);
|
||||
LifeSpan=1.0;
|
||||
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
if (!bDud) {
|
||||
Explode(HitLocation,Normal(HitLocation-Other.Location));
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
additionalDamagePart=0.250000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,81 @@
|
|||
class MeanZombieBloat extends NiceZombieBloat;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
/**
|
||||
* bAmIBarfing true if the bloat is in the barf animation
|
||||
*/
|
||||
var bool bAmIBarfing;
|
||||
/**
|
||||
* bileCoolDownTimer timer that counts to when the bloat will spawn another set of pile pellets
|
||||
* bileCoolDownMax max time in between pellet spawns
|
||||
*/
|
||||
var float bileCoolDownTimer,bileCoolDownMax;
|
||||
/**
|
||||
* Spawn extra sets of bile pellets here once the bile cool down timer
|
||||
* has reached the max limit
|
||||
*/
|
||||
simulated function Tick(float DeltaTime) {
|
||||
Super.Tick(DeltaTime);
|
||||
if(!bDecapitated && bAmIBarfing) {
bileCoolDownTimer+= DeltaTime;
if(bileCoolDownTimer >= bileCoolDownMax) {
SpawnTwoShots();
bileCoolDownTimer= 0.0;
}
|
||||
}
|
||||
}
|
||||
function Touch(Actor Other) {
|
||||
super.Touch(Other);
|
||||
if (Other.IsA('ShotgunBullet')) {
ShotgunBullet(Other).Damage = 0;
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A) {
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
return;
|
||||
if ( Physics == PHYS_Swimming ) {
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) {
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated ) {
bShotAnim = true;
SetAnimAction('ZombieBarfMoving');
RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
bMovingPukeAttack=true;
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none ) {
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
}
|
||||
}
|
||||
}
|
||||
//ZombieBarf animation triggers this
|
||||
function SpawnTwoShots() {
|
||||
super.SpawnTwoShots();
|
||||
bAmIBarfing= true;
|
||||
}
|
||||
simulated function AnimEnd(int Channel) {
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
|
||||
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
|
||||
super.AnimEnd(Channel);
|
||||
if(Sequence == 'ZombieBarf')
bAmIBarfing= false;
|
||||
}
|
||||
defaultproperties
|
||||
{
bileCoolDownMax=0.750000
HeadHealth=125.000000
MenuName="Mean Bloat"
Skins(0)=Combiner'MeanZedSkins.bloat_cmb'
|
||||
}
|
||||
class MeanZombieBloat extends NiceZombieBloat;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
/**
|
||||
* bAmIBarfing true if the bloat is in the barf animation
|
||||
*/
|
||||
var bool bAmIBarfing;
|
||||
/**
|
||||
* bileCoolDownTimer timer that counts to when the bloat will spawn another set of pile pellets
|
||||
* bileCoolDownMax max time in between pellet spawns
|
||||
*/
|
||||
var float bileCoolDownTimer,bileCoolDownMax;
|
||||
/**
|
||||
* Spawn extra sets of bile pellets here once the bile cool down timer
|
||||
* has reached the max limit
|
||||
*/
|
||||
simulated function Tick(float DeltaTime) {
|
||||
Super.Tick(DeltaTime);
|
||||
if(!bDecapitated && bAmIBarfing) {
|
||||
bileCoolDownTimer+= DeltaTime;
|
||||
if(bileCoolDownTimer >= bileCoolDownMax) {
|
||||
SpawnTwoShots();
|
||||
bileCoolDownTimer= 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
function Touch(Actor Other) {
|
||||
super.Touch(Other);
|
||||
if (Other.IsA('ShotgunBullet')) {
|
||||
ShotgunBullet(Other).Damage = 0;
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A) {
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
if ( Physics == PHYS_Swimming ) {
|
||||
SetAnimAction('Claw');
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) {
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
SetAnimAction('Claw');
|
||||
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated ) {
|
||||
bShotAnim = true;
|
||||
SetAnimAction('ZombieBarfMoving');
|
||||
RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
|
||||
bMovingPukeAttack=true;
|
||||
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
|
||||
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none ) {
|
||||
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
//ZombieBarf animation triggers this
|
||||
function SpawnTwoShots() {
|
||||
super.SpawnTwoShots();
|
||||
bAmIBarfing= true;
|
||||
}
|
||||
simulated function AnimEnd(int Channel) {
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
|
||||
GetAnimParams( ExpectingChannel, Sequence, Frame, Rate );
|
||||
super.AnimEnd(Channel);
|
||||
if(Sequence == 'ZombieBarf')
|
||||
bAmIBarfing= false;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
bileCoolDownMax=0.750000
|
||||
HeadHealth=125.000000
|
||||
MenuName="Mean Bloat"
|
||||
Skins(0)=Combiner'MeanZedSkins.bloat_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
class MeanZombieClot extends NiceZombieClot;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function int ModBodyDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
local bool bDecreaseDamage;
|
||||
// Decrease damage if needed
|
||||
bDecreaseDamage = false;
|
||||
if(damageType != none)
bDecreaseDamage = (headshotLevel <= 0.0) && damageType.default.bCheckForHeadShots;
|
||||
if(damageType != none && damageType.default.heatPart > 0)
bDecreaseDamage = false;
|
||||
if(bDecreaseDamage && HeadHealth > 0)
Damage *= 0.5;
|
||||
return super.ModBodyDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI, lockonTime);
|
||||
}
|
||||
defaultproperties
|
||||
{
MenuName="Mean Clot"
Skins(0)=Combiner'MeanZedSkins.clot_cmb'
|
||||
}
|
||||
class MeanZombieClot extends NiceZombieClot;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function int ModBodyDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
local bool bDecreaseDamage;
|
||||
// Decrease damage if needed
|
||||
bDecreaseDamage = false;
|
||||
if(damageType != none)
|
||||
bDecreaseDamage = (headshotLevel <= 0.0) && damageType.default.bCheckForHeadShots;
|
||||
if(damageType != none && damageType.default.heatPart > 0)
|
||||
bDecreaseDamage = false;
|
||||
if(bDecreaseDamage && HeadHealth > 0)
|
||||
Damage *= 0.5;
|
||||
return super.ModBodyDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI, lockonTime);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
MenuName="Mean Clot"
|
||||
Skins(0)=Combiner'MeanZedSkins.clot_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,46 @@
|
|||
class MeanZombieCrawler extends NiceZombieCrawler;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
simulated function PostBeginPlay() {
|
||||
super.PostBeginPlay();
|
||||
PounceSpeed = Rand(221)+330;
|
||||
MeleeRange = Rand(41)+50;
|
||||
}
|
||||
/**
|
||||
* Copied from ZombieCrawler.Bump() but changed damage type
|
||||
* to be the new poison damage type
|
||||
*/
|
||||
event Bump(actor Other) {
|
||||
if(bPouncing && KFHumanPawn(Other) != none)
Poison(KFHumanPawn(Other));
|
||||
super.Bump(Other);
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir) {
|
||||
local bool result;
|
||||
result= super.MeleeDamageTarget(hitdamage, pushdir);
|
||||
if(result && KFHumanPawn(Controller.Target) != none)
Poison(KFHumanPawn(Controller.Target));
|
||||
return result;
|
||||
}
|
||||
function Poison(KFHumanPawn poisonedPawn){
|
||||
local Inventory I;
|
||||
local bool bFoundPoison;
|
||||
if(poisonedPawn.Inventory != none){
for(I = poisonedPawn.Inventory; I != none; I = I.Inventory)
if(I != none && MeanPoisonInventory(I) != none){
bFoundPoison = true;
MeanPoisonInventory(I).poisonStartTime = Level.TimeSeconds;
}
|
||||
}
|
||||
if(!bFoundPoison){
I = Controller.Spawn(class<Inventory>(DynamicLoadObject("NicePack.MeanPoisonInventory", Class'Class')));
MeanPoisonInventory(I).poisonStartTime = Level.TimeSeconds;
I.GiveTo(poisonedPawn);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
GroundSpeed=190.000000
WaterSpeed=175.000000
MenuName="Mean Crawler"
Skins(0)=Combiner'MeanZedSkins.crawler_cmb'
|
||||
}
|
||||
class MeanZombieCrawler extends NiceZombieCrawler;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
simulated function PostBeginPlay() {
|
||||
super.PostBeginPlay();
|
||||
PounceSpeed = Rand(221)+330;
|
||||
MeleeRange = Rand(41)+50;
|
||||
}
|
||||
/**
|
||||
* Copied from ZombieCrawler.Bump() but changed damage type
|
||||
* to be the new poison damage type
|
||||
*/
|
||||
event Bump(actor Other) {
|
||||
if(bPouncing && KFHumanPawn(Other) != none)
|
||||
Poison(KFHumanPawn(Other));
|
||||
super.Bump(Other);
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir) {
|
||||
local bool result;
|
||||
result= super.MeleeDamageTarget(hitdamage, pushdir);
|
||||
if(result && KFHumanPawn(Controller.Target) != none)
|
||||
Poison(KFHumanPawn(Controller.Target));
|
||||
return result;
|
||||
}
|
||||
function Poison(KFHumanPawn poisonedPawn){
|
||||
local Inventory I;
|
||||
local bool bFoundPoison;
|
||||
if(poisonedPawn.Inventory != none){
|
||||
for(I = poisonedPawn.Inventory; I != none; I = I.Inventory)
|
||||
if(I != none && MeanPoisonInventory(I) != none){
|
||||
bFoundPoison = true;
|
||||
MeanPoisonInventory(I).poisonStartTime = Level.TimeSeconds;
|
||||
}
|
||||
}
|
||||
if(!bFoundPoison){
|
||||
I = Controller.Spawn(class<Inventory>(DynamicLoadObject("NicePack.MeanPoisonInventory", Class'Class')));
|
||||
MeanPoisonInventory(I).poisonStartTime = Level.TimeSeconds;
|
||||
I.GiveTo(poisonedPawn);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
GroundSpeed=190.000000
|
||||
WaterSpeed=175.000000
|
||||
MenuName="Mean Crawler"
|
||||
Skins(0)=Combiner'MeanZedSkins.crawler_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,104 @@
|
|||
class MeanZombieFleshPound extends NiceZombieFleshPound;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
state RageCharging
|
||||
{
|
||||
Ignores StartChargingFP;
|
||||
function bool CanGetOutOfWay()
|
||||
{
return false;
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
bChargingPlayer = true;
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
|
||||
RageEndTime = (Level.TimeSeconds + 15) + (FRand() * 18);
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function EndState()
|
||||
{
bChargingPlayer = false;
bFrustrated = false;
if(Controller != none)
NiceZombieFleshPoundController(Controller).RageFrustrationTimer = 0;
if( Health>0 && !bZapped )
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
|
||||
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
if( !bShotAnim )
{
SetGroundSpeed(OriginalGroundSpeed * 2.3);//2.0;
}
|
||||
// Keep the flesh pound moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim)
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function Bump( Actor Other )
|
||||
{
local float RageBumpDamage;
local KFMonster KFMonst;
|
||||
KFMonst = KFMonster(Other);
|
||||
// Hurt/Kill enemies that we run into while raging
if( !bShotAnim && KFMonst!=none && NiceZombieFleshPound(Other)==none && Pawn(Other).Health>0 )
{
// Random chance of doing obliteration damage
if( FRand() < 0.4 )
{
RageBumpDamage = 501;
}
else
{
RageBumpDamage = 450;
}
|
||||
RageBumpDamage *= KFMonst.PoundRageBumpDamScale;
|
||||
Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'NiceDamTypePoundCrushed');
}
else Global.Bump(Other);
|
||||
}
|
||||
// If fleshie hits his target on a charge, then he should settle down for abit.
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
local bool RetVal,bWasEnemy;
|
||||
bWasEnemy = (Controller.Target==Controller.Enemy);
RetVal = Super(NiceMonster).MeleeDamageTarget(hitdamage*1.75, pushdir*3);
// Only stop if you've successfully killed your target
if(Pawn(Controller.Target) == none)
return RetVal;
if( KFPawn(Controller.Target) != none && Pawn(Controller.Target).Health <= 0 && RetVal && bWasEnemy ){
GoToState('');
}
return RetVal;
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
MenuName="Mean FleshPound"
Skins(0)=Combiner'MeanZedSkins.fleshpound_cmb'
|
||||
}
|
||||
class MeanZombieFleshPound extends NiceZombieFleshPound;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
state RageCharging
|
||||
{
|
||||
Ignores StartChargingFP;
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
bChargingPlayer = true;
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
ClientChargingAnims();
|
||||
|
||||
RageEndTime = (Level.TimeSeconds + 15) + (FRand() * 18);
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
bChargingPlayer = false;
|
||||
bFrustrated = false;
|
||||
if(Controller != none)
|
||||
NiceZombieFleshPoundController(Controller).RageFrustrationTimer = 0;
|
||||
if( Health>0 && !bZapped )
|
||||
{
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
ClientChargingAnims();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
if( !bShotAnim )
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 2.3);//2.0;
|
||||
}
|
||||
|
||||
// Keep the flesh pound moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim)
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function Bump( Actor Other )
|
||||
{
|
||||
local float RageBumpDamage;
|
||||
local KFMonster KFMonst;
|
||||
|
||||
KFMonst = KFMonster(Other);
|
||||
|
||||
// Hurt/Kill enemies that we run into while raging
|
||||
if( !bShotAnim && KFMonst!=none && NiceZombieFleshPound(Other)==none && Pawn(Other).Health>0 )
|
||||
{
|
||||
// Random chance of doing obliteration damage
|
||||
if( FRand() < 0.4 )
|
||||
{
|
||||
RageBumpDamage = 501;
|
||||
}
|
||||
else
|
||||
{
|
||||
RageBumpDamage = 450;
|
||||
}
|
||||
|
||||
RageBumpDamage *= KFMonst.PoundRageBumpDamScale;
|
||||
|
||||
Other.TakeDamage(RageBumpDamage, self, Other.Location, Velocity * Other.Mass, class'NiceDamTypePoundCrushed');
|
||||
}
|
||||
else Global.Bump(Other);
|
||||
}
|
||||
// If fleshie hits his target on a charge, then he should settle down for abit.
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir)
|
||||
{
|
||||
local bool RetVal,bWasEnemy;
|
||||
|
||||
bWasEnemy = (Controller.Target==Controller.Enemy);
|
||||
RetVal = Super(NiceMonster).MeleeDamageTarget(hitdamage*1.75, pushdir*3);
|
||||
// Only stop if you've successfully killed your target
|
||||
if(Pawn(Controller.Target) == none)
|
||||
return RetVal;
|
||||
if( KFPawn(Controller.Target) != none && Pawn(Controller.Target).Health <= 0 && RetVal && bWasEnemy ){
|
||||
GoToState('');
|
||||
}
|
||||
return RetVal;
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
MenuName="Mean FleshPound"
|
||||
Skins(0)=Combiner'MeanZedSkins.fleshpound_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,40 @@
|
|||
class MeanZombieGorefast extends NiceZombieGorefast;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
var float minRageDist;
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
function RangedAttack(Actor A) {
|
||||
Super(NiceMonster).RangedAttack(A);
|
||||
if(!bShotAnim && !bDecapitated && VSize(A.Location-Location) <= minRageDist)
GoToState('RunningState');
|
||||
}
|
||||
state RunningState {
|
||||
function RangedAttack(Actor A){
if(bShotAnim || Physics == PHYS_Swimming)
return;
else if(CanAttack(A)){
bShotAnim = true;
|
||||
//Always do the charging melee attack
SetAnimAction('ClawAndMove');
RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0);
return;
}
|
||||
}
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if(Controller != none && Controller.Target != none && VSize(Controller.Target.Location - Location) < minRageDist){
Sleep(0.5 + FRand() * 0.5);
GoTo('CheckCharge');
|
||||
}
|
||||
else
GoToState('');
|
||||
}
|
||||
defaultproperties
|
||||
{
minRageDist=1400.000000
MenuName="Mean Gorefast"
Skins(0)=Combiner'MeanZedSkins.gorefast_cmb'
|
||||
}
|
||||
class MeanZombieGorefast extends NiceZombieGorefast;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
var float minRageDist;
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
function RangedAttack(Actor A) {
|
||||
Super(NiceMonster).RangedAttack(A);
|
||||
if(!bShotAnim && !bDecapitated && VSize(A.Location-Location) <= minRageDist)
|
||||
GoToState('RunningState');
|
||||
}
|
||||
state RunningState {
|
||||
function RangedAttack(Actor A){
|
||||
if(bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if(CanAttack(A)){
|
||||
bShotAnim = true;
|
||||
|
||||
//Always do the charging melee attack
|
||||
SetAnimAction('ClawAndMove');
|
||||
RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if(Controller != none && Controller.Target != none && VSize(Controller.Target.Location - Location) < minRageDist){
|
||||
Sleep(0.5 + FRand() * 0.5);
|
||||
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
GoToState('');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
minRageDist=1400.000000
|
||||
MenuName="Mean Gorefast"
|
||||
Skins(0)=Combiner'MeanZedSkins.gorefast_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,91 @@
|
|||
class MeanZombieHusk extends NiceZombieHusk;
|
||||
#exec OBJ LOAD FILE=NicePackT.utx
|
||||
var int consecutiveShots, totalShots, maxNormalShots;
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
|
||||
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
totalShots = maxNormalShots;
|
||||
}
|
||||
function SpawnTwoShots() {
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
local KFMonsterController KFMonstControl;
|
||||
if(Controller != none && KFDoorMover(Controller.Target) != none){
Controller.Target.TakeDamage(22, Self, Location, vect(0,0,0), Class'DamTypeVomit');
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = GetBoneCoords('Barrel').Origin;
|
||||
if (!SavedFireProperties.bInitialized){
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = HuskFireProjClass;
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 65535;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = true;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = False;
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
if(Controller != none)
FireRotation = Controller.AdjustAim(SavedFireProperties, FireStart, 600);
|
||||
foreach DynamicActors(class'KFMonsterController', KFMonstControl){
if(KFMonstControl != controller){
if(PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75){
KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation),FireStart);
}
}
|
||||
}
|
||||
Spawn(HuskFireProjClass, Self,, FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
function RangedAttack(Actor A) {
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
return;
|
||||
if ( Physics == PHYS_Swimming ) {
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) {
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if((KFDoorMover(A) != none ||
(!Region.Zone.bDistanceFog && VSize(A.Location-Location) <= 65535) ||
(Region.Zone.bDistanceFog && VSizeSquared(A.Location-Location) < (Square(Region.Zone.DistanceFogEnd) * 0.8))) // Make him come out of the fog a bit
&& !bDecapitated ) {
bShotAnim = true;
|
||||
SetAnimAction('ShootBurns');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
//Increment the number of consecutive shtos taken and apply the cool down if needed
totalShots ++;
consecutiveShots ++;
if(consecutiveShots < 3 && totalShots > maxNormalShots)
NextFireProjectileTime = Level.TimeSeconds;
else{
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
consecutiveShots = 0;
}
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
maxNormalShots=3
HuskFireProjClass=Class'NicePack.MeanHuskFireProjectile'
remainingStuns=1
MenuName="Mean Husk"
ControllerClass=Class'NicePack.MeanZombieHuskController'
Skins(0)=Texture'NicePackT.MonsterMeanHusk.burns_tatters'
Skins(1)=Shader'NicePackT.MonsterMeanHusk.burns_shdr'
|
||||
}
|
||||
class MeanZombieHusk extends NiceZombieHusk;
|
||||
#exec OBJ LOAD FILE=NicePackT.utx
|
||||
var int consecutiveShots, totalShots, maxNormalShots;
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
|
||||
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
totalShots = maxNormalShots;
|
||||
}
|
||||
function SpawnTwoShots() {
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
local KFMonsterController KFMonstControl;
|
||||
if(Controller != none && KFDoorMover(Controller.Target) != none){
|
||||
Controller.Target.TakeDamage(22, Self, Location, vect(0,0,0), Class'DamTypeVomit');
|
||||
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = GetBoneCoords('Barrel').Origin;
|
||||
if (!SavedFireProperties.bInitialized){
|
||||
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
|
||||
SavedFireProperties.ProjectileClass = HuskFireProjClass;
|
||||
SavedFireProperties.WarnTargetPct = 1;
|
||||
SavedFireProperties.MaxRange = 65535;
|
||||
SavedFireProperties.bTossed = False;
|
||||
SavedFireProperties.bTrySplash = true;
|
||||
SavedFireProperties.bLeadTarget = True;
|
||||
SavedFireProperties.bInstantHit = False;
|
||||
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
if(Controller != none)
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties, FireStart, 600);
|
||||
foreach DynamicActors(class'KFMonsterController', KFMonstControl){
|
||||
if(KFMonstControl != controller){
|
||||
if(PointDistToLine(KFMonstControl.Pawn.Location, vector(FireRotation), FireStart) < 75){
|
||||
KFMonstControl.GetOutOfTheWayOfShot(vector(FireRotation),FireStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
Spawn(HuskFireProjClass, Self,, FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
function RangedAttack(Actor A) {
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
if ( Physics == PHYS_Swimming ) {
|
||||
SetAnimAction('Claw');
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius ) {
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
SetAnimAction('Claw');
|
||||
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if((KFDoorMover(A) != none ||
|
||||
(!Region.Zone.bDistanceFog && VSize(A.Location-Location) <= 65535) ||
|
||||
(Region.Zone.bDistanceFog && VSizeSquared(A.Location-Location) < (Square(Region.Zone.DistanceFogEnd) * 0.8))) // Make him come out of the fog a bit
|
||||
&& !bDecapitated ) {
|
||||
bShotAnim = true;
|
||||
|
||||
SetAnimAction('ShootBurns');
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
|
||||
//Increment the number of consecutive shtos taken and apply the cool down if needed
|
||||
totalShots ++;
|
||||
consecutiveShots ++;
|
||||
if(consecutiveShots < 3 && totalShots > maxNormalShots)
|
||||
NextFireProjectileTime = Level.TimeSeconds;
|
||||
else{
|
||||
NextFireProjectileTime = Level.TimeSeconds + ProjectileFireInterval + (FRand() * 2.0);
|
||||
consecutiveShots = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
maxNormalShots=3
|
||||
HuskFireProjClass=Class'NicePack.MeanHuskFireProjectile'
|
||||
remainingStuns=1
|
||||
MenuName="Mean Husk"
|
||||
ControllerClass=Class'NicePack.MeanZombieHuskController'
|
||||
Skins(0)=Texture'NicePackT.MonsterMeanHusk.burns_tatters'
|
||||
Skins(1)=Shader'NicePackT.MonsterMeanHusk.burns_shdr'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,193 @@
|
|||
class MeanZombieHuskController extends NiceZombieHuskController;
|
||||
var float aimAtFeetZDelta;
|
||||
function bool DefendMelee(float Dist) {
|
||||
return (Dist < 1000);
|
||||
}
|
||||
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror) {
|
||||
local rotator FireRotation, TargetLook;
|
||||
local float FireDist, TargetDist, ProjSpeed;
|
||||
local actor HitActor;
|
||||
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
|
||||
local int realYaw;
|
||||
local bool bDefendCloseRange, bClean, bLeadTargetNow;
|
||||
local bool bWantsToAimAtFeet;
|
||||
if ( FiredAmmunition.ProjectileClass != none )
projspeed = FiredAmmunition.ProjectileClass.default.speed;
|
||||
// make sure bot has a valid target
|
||||
if ( Target == none ) {
Target = Enemy;
if ( Target == none )
return Rotation;
|
||||
}
|
||||
FireSpot = Target.Location;
|
||||
TargetDist = VSize(Target.Location - Pawn.Location);
|
||||
// perfect aim at stationary objects
|
||||
if ( Pawn(Target) == none ) {
if ( !FiredAmmunition.bTossed )
return rotator(Target.Location - projstart);
else {
FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);
SetRotation(Rotator(FireDir));
return Rotation;
}
|
||||
}
|
||||
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
|
||||
bDefendCloseRange = ( (Target == Enemy) && DefendMelee(TargetDist) );
|
||||
aimerror = AdjustAimError(aimerror,TargetDist,bDefendCloseRange,FiredAmmunition.bInstantHit, bLeadTargetNow);
|
||||
// lead target with non instant hit projectiles
|
||||
if ( bLeadTargetNow ) {
TargetVel = Target.Velocity;
// hack guess at projecting falling velocity of target
if ( Target.Physics == PHYS_Falling) {
if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z ) {
TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
} else {
TargetVel.Z = FMin(0, TargetVel.Z);
}
}
// more or less lead target (with some random variation)
FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
/**
* If the target is within 1000uu, offset the Z coordinate of the
* FireSpot vector with aimAtFeetZDelta. Otherwise, the husk will
* aim at behind the target, not at his feet.
*/
if (aimAtFeetZDelta != 0.0 && Target.Physics == PHYS_Falling && bDefendCloseRange) {
FireSpot.Z= Pawn.Location.Z + aimAtFeetZDelta;
}
|
||||
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) ) {
// don't always lead far away targets, especially if they are moving sideways with respect to the bot
TargetLook = Target.Rotation;
if ( Target.Physics == PHYS_Walking )
TargetLook.Pitch = 0;
bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
}
else // make sure that bot isn't leading into a wall
bClean = FastTrace(FireSpot, ProjStart);
if ( !bClean) {
// reduce amount of leading
if ( FRand() < 0.3 )
FireSpot = Target.Location;
else
FireSpot = 0.5 * (FireSpot + Target.Location);
}
|
||||
}
|
||||
bClean = false; //so will fail first check unless shooting at feet
|
||||
// Randomly determine if we should try and splash damage with the fire projectile
|
||||
if( FiredAmmunition.bTrySplash ) {
if( Skill < 2.0 ) {
if(FRand() > 0.85) {
bWantsToAimAtFeet = true;
}
}
else if( Skill < 3.0 ) {
if(FRand() > 0.5) {
bWantsToAimAtFeet = true;
}
}
else if( Skill >= 3.0 ) {
if(FRand() > 0.25) {
bWantsToAimAtFeet = true;
}
}
|
||||
}
|
||||
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != none) && (((Target.Physics == PHYS_Falling)
&& (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z)
&& (bDefendCloseRange || bWantsToAimAtFeet))) ) {
HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
|
||||
bClean = (HitActor == none);
//So if we're too close, and not jumping, bClean is false
//same distance but jumping, bClean is true
if ( !bClean ) {
FireSpot = HitLocation + vect(0,0,3);
bClean = FastTrace(FireSpot, ProjStart);
}
else
bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
/**
* Update the aimAtFeetZDelta variable with the appropriate offset
* once the Husk decides to aim at the target's feet. Update the
* default property so all Super Husks can access it
*/
if (bClean && TargetDist > 625.0) {
aimAtFeetZDelta= FireSpot.Z - Pawn.Location.Z;
}
|
||||
}
|
||||
if ( !bClean ) {
//try middle
FireSpot.Z = Target.Location.Z;
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid ) {
FireSpot = LastSeenPos;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none ) {
bCanFire = false;
FireSpot += 2 * Target.CollisionHeight * HitNormal;
}
bClean = true;
|
||||
}
|
||||
if( !bClean ) {
// try head
FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( !bClean && (Target == Enemy) && bEnemyInfoValid ) {
FireSpot = LastSeenPos;
if ( Pawn.Location.Z >= LastSeenPos.Z )
FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none ) {
FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
if ( Monster(Pawn).SplashDamage() && (Skill >= 4) ) {
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
}
bCanFire = false;
}
|
||||
}
|
||||
// adjust for toss distance
|
||||
if ( FiredAmmunition.bTossed ) {
FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
|
||||
}
|
||||
else {
FireDir = FireSpot - ProjStart;
|
||||
}
|
||||
FireRotation = Rotator(FireDir);
|
||||
realYaw = FireRotation.Yaw;
|
||||
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
|
||||
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
// avoid shooting into wall
|
||||
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none ) {
if ( HitNormal.Z < 0.7 ) {
FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
FireDir = vector(FireRotation);
FireSpot = ProjStart + FireDist * FireDir;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
}
if ( HitActor != none ) {
FireSpot += HitNormal * 2 * Target.CollisionHeight;
if ( Skill >= 4 ) {
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
FireSpot += Target.CollisionHeight * HitNormal;
}
FireDir = Normal(FireSpot - ProjStart);
FireRotation = rotator(FireDir);
}
|
||||
}
|
||||
//Make it so the Husk always shoots the ground it the target is close
|
||||
SetRotation(FireRotation);
|
||||
return FireRotation;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class MeanZombieHuskController extends NiceZombieHuskController;
|
||||
var float aimAtFeetZDelta;
|
||||
function bool DefendMelee(float Dist) {
|
||||
return (Dist < 1000);
|
||||
}
|
||||
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror) {
|
||||
local rotator FireRotation, TargetLook;
|
||||
local float FireDist, TargetDist, ProjSpeed;
|
||||
local actor HitActor;
|
||||
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
|
||||
local int realYaw;
|
||||
local bool bDefendCloseRange, bClean, bLeadTargetNow;
|
||||
local bool bWantsToAimAtFeet;
|
||||
if ( FiredAmmunition.ProjectileClass != none )
|
||||
projspeed = FiredAmmunition.ProjectileClass.default.speed;
|
||||
// make sure bot has a valid target
|
||||
if ( Target == none ) {
|
||||
Target = Enemy;
|
||||
if ( Target == none )
|
||||
return Rotation;
|
||||
}
|
||||
FireSpot = Target.Location;
|
||||
TargetDist = VSize(Target.Location - Pawn.Location);
|
||||
// perfect aim at stationary objects
|
||||
if ( Pawn(Target) == none ) {
|
||||
if ( !FiredAmmunition.bTossed )
|
||||
return rotator(Target.Location - projstart);
|
||||
else {
|
||||
FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);
|
||||
SetRotation(Rotator(FireDir));
|
||||
return Rotation;
|
||||
}
|
||||
}
|
||||
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
|
||||
bDefendCloseRange = ( (Target == Enemy) && DefendMelee(TargetDist) );
|
||||
aimerror = AdjustAimError(aimerror,TargetDist,bDefendCloseRange,FiredAmmunition.bInstantHit, bLeadTargetNow);
|
||||
// lead target with non instant hit projectiles
|
||||
if ( bLeadTargetNow ) {
|
||||
TargetVel = Target.Velocity;
|
||||
// hack guess at projecting falling velocity of target
|
||||
if ( Target.Physics == PHYS_Falling) {
|
||||
if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z ) {
|
||||
TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
|
||||
} else {
|
||||
TargetVel.Z = FMin(0, TargetVel.Z);
|
||||
}
|
||||
}
|
||||
// more or less lead target (with some random variation)
|
||||
FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
|
||||
FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
|
||||
/**
|
||||
* If the target is within 1000uu, offset the Z coordinate of the
|
||||
* FireSpot vector with aimAtFeetZDelta. Otherwise, the husk will
|
||||
* aim at behind the target, not at his feet.
|
||||
*/
|
||||
if (aimAtFeetZDelta != 0.0 && Target.Physics == PHYS_Falling && bDefendCloseRange) {
|
||||
FireSpot.Z= Pawn.Location.Z + aimAtFeetZDelta;
|
||||
}
|
||||
|
||||
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) ) {
|
||||
// don't always lead far away targets, especially if they are moving sideways with respect to the bot
|
||||
TargetLook = Target.Rotation;
|
||||
if ( Target.Physics == PHYS_Walking )
|
||||
TargetLook.Pitch = 0;
|
||||
bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
|
||||
}
|
||||
else // make sure that bot isn't leading into a wall
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
if ( !bClean) {
|
||||
// reduce amount of leading
|
||||
if ( FRand() < 0.3 )
|
||||
FireSpot = Target.Location;
|
||||
else
|
||||
FireSpot = 0.5 * (FireSpot + Target.Location);
|
||||
}
|
||||
}
|
||||
bClean = false; //so will fail first check unless shooting at feet
|
||||
// Randomly determine if we should try and splash damage with the fire projectile
|
||||
if( FiredAmmunition.bTrySplash ) {
|
||||
if( Skill < 2.0 ) {
|
||||
if(FRand() > 0.85) {
|
||||
bWantsToAimAtFeet = true;
|
||||
}
|
||||
}
|
||||
else if( Skill < 3.0 ) {
|
||||
if(FRand() > 0.5) {
|
||||
bWantsToAimAtFeet = true;
|
||||
}
|
||||
}
|
||||
else if( Skill >= 3.0 ) {
|
||||
if(FRand() > 0.25) {
|
||||
bWantsToAimAtFeet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != none) && (((Target.Physics == PHYS_Falling)
|
||||
&& (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z)
|
||||
&& (bDefendCloseRange || bWantsToAimAtFeet))) ) {
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
|
||||
|
||||
bClean = (HitActor == none);
|
||||
//So if we're too close, and not jumping, bClean is false
|
||||
//same distance but jumping, bClean is true
|
||||
if ( !bClean ) {
|
||||
FireSpot = HitLocation + vect(0,0,3);
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
else
|
||||
bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
|
||||
/**
|
||||
* Update the aimAtFeetZDelta variable with the appropriate offset
|
||||
* once the Husk decides to aim at the target's feet. Update the
|
||||
* default property so all Super Husks can access it
|
||||
*/
|
||||
if (bClean && TargetDist > 625.0) {
|
||||
aimAtFeetZDelta= FireSpot.Z - Pawn.Location.Z;
|
||||
}
|
||||
}
|
||||
if ( !bClean ) {
|
||||
//try middle
|
||||
FireSpot.Z = Target.Location.Z;
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid ) {
|
||||
FireSpot = LastSeenPos;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none ) {
|
||||
bCanFire = false;
|
||||
FireSpot += 2 * Target.CollisionHeight * HitNormal;
|
||||
}
|
||||
bClean = true;
|
||||
}
|
||||
if( !bClean ) {
|
||||
// try head
|
||||
FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( !bClean && (Target == Enemy) && bEnemyInfoValid ) {
|
||||
FireSpot = LastSeenPos;
|
||||
if ( Pawn.Location.Z >= LastSeenPos.Z )
|
||||
FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none ) {
|
||||
FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
|
||||
if ( Monster(Pawn).SplashDamage() && (Skill >= 4) ) {
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
|
||||
}
|
||||
bCanFire = false;
|
||||
}
|
||||
}
|
||||
// adjust for toss distance
|
||||
if ( FiredAmmunition.bTossed ) {
|
||||
FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
|
||||
}
|
||||
else {
|
||||
FireDir = FireSpot - ProjStart;
|
||||
}
|
||||
FireRotation = Rotator(FireDir);
|
||||
realYaw = FireRotation.Yaw;
|
||||
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
|
||||
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
// avoid shooting into wall
|
||||
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none ) {
|
||||
if ( HitNormal.Z < 0.7 ) {
|
||||
FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
}
|
||||
if ( HitActor != none ) {
|
||||
FireSpot += HitNormal * 2 * Target.CollisionHeight;
|
||||
if ( Skill >= 4 ) {
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
FireSpot += Target.CollisionHeight * HitNormal;
|
||||
}
|
||||
FireDir = Normal(FireSpot - ProjStart);
|
||||
FireRotation = rotator(FireDir);
|
||||
}
|
||||
}
|
||||
//Make it so the Husk always shoots the ground it the target is close
|
||||
SetRotation(FireRotation);
|
||||
return FireRotation;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,46 @@
|
|||
class MeanZombieScrake extends NiceZombieScrake;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function RangedAttack(Actor A){
|
||||
Super.RangedAttack(A);
|
||||
if(!bShotAnim){
if(bConfusedState)
return;
if(float(Health) / HealthMax < 0.75 || lastStunTime >= 0.0){
MovementAnims[0] = 'ChargeF';
GoToState('RunningState');
}
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction){
|
||||
if(Role < Role_AUTHORITY && NewAction == 'ChargeF')
PlayAnim('ChargeF', GetOriginalGroundSpeed() * 3.5);
|
||||
else
super.SetAnimAction(NewAction);
|
||||
}
|
||||
simulated function Unstun(){
|
||||
bCharging = true;
|
||||
MovementAnims[0] = 'ChargeF';
|
||||
super.Unstun();
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if(bIsStunned && Health > 0 && (headshotLevel <= 0.0) && Level.TimeSeconds > LastStunTime + 0.1)
Unstun();
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator){
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bIsStunned && Health > 0 && Damage > 150 && Level.TimeSeconds > LastStunTime + 0.1)
Unstun();
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if((ClassIsChildOf(damageType, class 'DamTypeMelee') || ClassIsChildOf(damageType, class 'NiceDamageTypeVetBerserker'))
&& !KFPRI.ClientVeteranSkill.Static.CanMeleeStun() && (headshotLevel <= 0.0) && flinchScore < 250)
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
defaultproperties
|
||||
{
MenuName="Mean Scrake"
Skins(0)=Shader'MeanZedSkins.scrake_FB'
Skins(1)=TexPanner'MeanZedSkins.scrake_saw_panner'
|
||||
}
|
||||
class MeanZombieScrake extends NiceZombieScrake;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function RangedAttack(Actor A){
|
||||
Super.RangedAttack(A);
|
||||
if(!bShotAnim){
|
||||
if(bConfusedState)
|
||||
return;
|
||||
if(float(Health) / HealthMax < 0.75 || lastStunTime >= 0.0){
|
||||
MovementAnims[0] = 'ChargeF';
|
||||
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction){
|
||||
if(Role < Role_AUTHORITY && NewAction == 'ChargeF')
|
||||
PlayAnim('ChargeF', GetOriginalGroundSpeed() * 3.5);
|
||||
else
|
||||
super.SetAnimAction(NewAction);
|
||||
}
|
||||
simulated function Unstun(){
|
||||
bCharging = true;
|
||||
MovementAnims[0] = 'ChargeF';
|
||||
super.Unstun();
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if(bIsStunned && Health > 0 && (headshotLevel <= 0.0) && Level.TimeSeconds > LastStunTime + 0.1)
|
||||
Unstun();
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator){
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bIsStunned && Health > 0 && Damage > 150 && Level.TimeSeconds > LastStunTime + 0.1)
|
||||
Unstun();
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if((ClassIsChildOf(damageType, class 'DamTypeMelee') || ClassIsChildOf(damageType, class 'NiceDamageTypeVetBerserker'))
|
||||
&& !KFPRI.ClientVeteranSkill.Static.CanMeleeStun() && (headshotLevel <= 0.0) && flinchScore < 250)
|
||||
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
MenuName="Mean Scrake"
|
||||
Skins(0)=Shader'MeanZedSkins.scrake_FB'
|
||||
Skins(1)=TexPanner'MeanZedSkins.scrake_saw_panner'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
class MeanZombieSiren extends NiceZombieSiren;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
defaultproperties
|
||||
{
ScreamRadius=800
ScreamForce=-250000
MenuName="Mean Siren"
Skins(0)=FinalBlend'MeanZedSkins.siren_hair_fb'
Skins(1)=Combiner'MeanZedSkins.siren_cmb'
|
||||
}
|
||||
class MeanZombieSiren extends NiceZombieSiren;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
defaultproperties
|
||||
{
|
||||
ScreamRadius=800
|
||||
ScreamForce=-250000
|
||||
MenuName="Mean Siren"
|
||||
Skins(0)=FinalBlend'MeanZedSkins.siren_hair_fb'
|
||||
Skins(1)=Combiner'MeanZedSkins.siren_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,128 +1,236 @@
|
|||
class MeanZombieStalker extends NiceZombieStalker;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
Super(NiceMonster).Tick(DeltaTime);
|
||||
if(Role == ROLE_Authority && bShotAnim && !bWaitForAnim){
if( LookTarget!=none ) {
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
|
||||
}
|
||||
if(Level.NetMode == NM_DedicatedServer)
return; // Servers aren't interested in this info.
|
||||
if(bZapped){
// Make sure we check if we need to be cloaked as soon as the zap wears off
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
if(LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() &&
VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0) // 640000 = 800 Units
bSpotted = True;
else
bSpotted = false;
|
||||
if(!bSpotted && !bCloaked && Skins[0] != Combiner'MeanZedSkins.stalker_cmb')
UncloakStalker();
else if (Level.TimeSeconds - LastUncloakTime > 1.2){
// if we're uberbrite, turn down the light
if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' ){
bUnlit = false;
CloakStalker();
}
else if(Skins[0] != Shader'MeanZedSkins.stalker_invisible')
CloakStalker();
}
|
||||
}
|
||||
}
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if ( bSpotted )
|
||||
{
if( Level.NetMode == NM_DedicatedServer )
return;
|
||||
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
|
||||
}
|
||||
if ( !bDecapitated ) // No head, no cloak, honey. updated : Being charred means no cloak either :D Not.
|
||||
{
Visibility = 1;
bCloaked = true;
|
||||
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
Skins[0] = Shader'MeanZedSkins.stalker_invisible';
Skins[1] = Shader'MeanZedSkins.stalker_invisible';
|
||||
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
|
||||
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
LastUncloakTime = Level.TimeSeconds;
|
||||
Visibility = default.Visibility;
bCloaked = false;
bUnlit = false;
|
||||
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
if ( Skins[0] != Combiner'MeanZedSkins.stalker_cmb' )
{
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
|
||||
}
|
||||
}
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
super(NiceMonster).SetZappedBehavior();
|
||||
bUnlit = false;
|
||||
// Handle setting the zed to uncloaked so the zapped overlay works properly
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
|
||||
}
|
||||
}
|
||||
|
||||
function RangedAttack(Actor A) {
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) ) {
bShotAnim = true;
SetAnimAction('ClawAndMove');
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
return;
|
||||
}
|
||||
}
|
||||
// Copied from the Gorefast code
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction) {
|
||||
if( NewAction=='' )
Return;
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
bWaitForAnim= false;
|
||||
|
||||
if( Level.NetMode!=NM_Client ) {
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Copied from the Gorefast code, updated with the stalker attacks
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName ) {
|
||||
local int meleeAnimIndex;
|
||||
if( AnimName == 'ClawAndMove' ) {
meleeAnimIndex = Rand(3);
AnimName = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') {
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir) {
|
||||
local bool result;
|
||||
local float effectStrenght;
|
||||
local NiceHumanPawn targetPawn;
|
||||
result = Super(NiceMonster).MeleeDamageTarget(hitdamage, pushdir);
|
||||
targetPawn = NiceHumanPawn(Controller.Target);
|
||||
if(result && targetPawn != none && (targetPawn.hmgShieldLevel <= 0 ||
!class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(targetPawn.Controller),
class'NiceSkillEnforcerFullCounter')) ){
if(targetPawn.ShieldStrength > 100)
return result;
else if(targetPawn.ShieldStrength < 0)
effectStrenght = 1.0;
else
effectStrenght = (100 - targetPawn.ShieldStrength) * 0.01;
class'MeanReplicationInfo'.static
.findSZri(targetPawn.PlayerReplicationInfo)
.setBleeding(Self, effectStrenght);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super(NiceMonster).RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super(NiceMonster).PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'MeanZedSkins.stalker_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'MeanZedSkins.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'MeanZedSkins.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'MeanZedSkins.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'MeanZedSkins.stalker_invisible');
|
||||
myLevel.AddPrecacheMaterial(Combiner'MeanZedSkins.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'MeanZedSkins.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'MeanZedSkins.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
defaultproperties
|
||||
{
MeleeDamage=6
MenuName="Mean Stalker"
Skins(0)=Shader'MeanZedSkins.stalker_invisible'
Skins(1)=Shader'MeanZedSkins.stalker_invisible'
|
||||
}
|
||||
class MeanZombieStalker extends NiceZombieStalker;
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
Super(NiceMonster).Tick(DeltaTime);
|
||||
if(Role == ROLE_Authority && bShotAnim && !bWaitForAnim){
|
||||
if( LookTarget!=none ) {
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
if(Level.NetMode == NM_DedicatedServer)
|
||||
return; // Servers aren't interested in this info.
|
||||
if(bZapped){
|
||||
// Make sure we check if we need to be cloaked as soon as the zap wears off
|
||||
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
|
||||
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
|
||||
if(LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() &&
|
||||
VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0) // 640000 = 800 Units
|
||||
bSpotted = True;
|
||||
else
|
||||
bSpotted = false;
|
||||
|
||||
if(!bSpotted && !bCloaked && Skins[0] != Combiner'MeanZedSkins.stalker_cmb')
|
||||
UncloakStalker();
|
||||
else if (Level.TimeSeconds - LastUncloakTime > 1.2){
|
||||
// if we're uberbrite, turn down the light
|
||||
if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' ){
|
||||
bUnlit = false;
|
||||
CloakStalker();
|
||||
}
|
||||
else if(Skins[0] != Shader'MeanZedSkins.stalker_invisible')
|
||||
CloakStalker();
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( bSpotted )
|
||||
{
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
return;
|
||||
|
||||
Skins[0] = Finalblend'KFX.StalkerGlow';
|
||||
Skins[1] = Finalblend'KFX.StalkerGlow';
|
||||
bUnlit = true;
|
||||
return;
|
||||
}
|
||||
if ( !bDecapitated ) // No head, no cloak, honey. updated : Being charred means no cloak either :D Not.
|
||||
{
|
||||
Visibility = 1;
|
||||
bCloaked = true;
|
||||
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
Return;
|
||||
|
||||
Skins[0] = Shader'MeanZedSkins.stalker_invisible';
|
||||
Skins[1] = Shader'MeanZedSkins.stalker_invisible';
|
||||
|
||||
// Invisible - no shadow
|
||||
if(PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = false;
|
||||
if(RealTimeShadow != none)
|
||||
RealTimeShadow.Destroy();
|
||||
|
||||
// Remove/disallow projectors on invisible people
|
||||
Projectors.Remove(0, Projectors.Length);
|
||||
bAcceptsProjectors = false;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
|
||||
LastUncloakTime = Level.TimeSeconds;
|
||||
|
||||
Visibility = default.Visibility;
|
||||
bCloaked = false;
|
||||
bUnlit = false;
|
||||
|
||||
// 25% chance of our Enemy saying something about us being invisible
|
||||
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
|
||||
PlayerController(Controller.Enemy.Controller)!=none )
|
||||
{
|
||||
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
|
||||
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
|
||||
}
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
Return;
|
||||
|
||||
if ( Skins[0] != Combiner'MeanZedSkins.stalker_cmb' )
|
||||
{
|
||||
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
|
||||
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
|
||||
if (PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = true;
|
||||
|
||||
bAcceptsProjectors = true;
|
||||
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
super(NiceMonster).SetZappedBehavior();
|
||||
bUnlit = false;
|
||||
// Handle setting the zed to uncloaked so the zapped overlay works properly
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
|
||||
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
|
||||
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
|
||||
if (PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = true;
|
||||
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
|
||||
}
|
||||
}
|
||||
|
||||
function RangedAttack(Actor A) {
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( CanAttack(A) ) {
|
||||
bShotAnim = true;
|
||||
SetAnimAction('ClawAndMove');
|
||||
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Copied from the Gorefast code
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction) {
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
bWaitForAnim= false;
|
||||
|
||||
if( Level.NetMode!=NM_Client ) {
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Copied from the Gorefast code, updated with the stalker attacks
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName ) {
|
||||
local int meleeAnimIndex;
|
||||
if( AnimName == 'ClawAndMove' ) {
|
||||
meleeAnimIndex = Rand(3);
|
||||
AnimName = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') {
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
function bool MeleeDamageTarget(int hitdamage, vector pushdir) {
|
||||
local bool result;
|
||||
local float effectStrenght;
|
||||
local NiceHumanPawn targetPawn;
|
||||
result = Super(NiceMonster).MeleeDamageTarget(hitdamage, pushdir);
|
||||
targetPawn = NiceHumanPawn(Controller.Target);
|
||||
if(result && targetPawn != none && (targetPawn.hmgShieldLevel <= 0 ||
|
||||
!class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(targetPawn.Controller),
|
||||
class'NiceSkillEnforcerFullCounter')) ){
|
||||
if(targetPawn.ShieldStrength > 100)
|
||||
return result;
|
||||
else if(targetPawn.ShieldStrength < 0)
|
||||
effectStrenght = 1.0;
|
||||
else
|
||||
effectStrenght = (100 - targetPawn.ShieldStrength) * 0.01;
|
||||
class'MeanReplicationInfo'.static
|
||||
.findSZri(targetPawn.PlayerReplicationInfo)
|
||||
.setBleeding(Self, effectStrenght);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super(NiceMonster).RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
|
||||
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
|
||||
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super(NiceMonster).PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
|
||||
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
|
||||
Skins[1] = FinalBlend'MeanZedSkins.stalker_fb';
|
||||
Skins[0] = Combiner'MeanZedSkins.stalker_cmb';
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'MeanZedSkins.stalker_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'MeanZedSkins.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'MeanZedSkins.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'MeanZedSkins.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'MeanZedSkins.stalker_invisible');
|
||||
myLevel.AddPrecacheMaterial(Combiner'MeanZedSkins.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'MeanZedSkins.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'MeanZedSkins.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
MeleeDamage=6
|
||||
MenuName="Mean Stalker"
|
||||
Skins(0)=Shader'MeanZedSkins.stalker_invisible'
|
||||
Skins(1)=Shader'MeanZedSkins.stalker_invisible'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class NiceDamTypeStalkerBleed extends NiceZedSlashingDamageType;
|
||||
defaultproperties
|
||||
{
bArmorStops=False
|
||||
}
|
||||
class NiceDamTypeStalkerBleed extends NiceZedSlashingDamageType;
|
||||
defaultproperties
|
||||
{
|
||||
bArmorStops=False
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +1,82 @@
|
|||
// Copy pasted from super zombies mutator with small alterations
|
||||
class MeanReplicationInfo extends ReplicationInfo;
|
||||
struct BleedingState {
|
||||
var float nextBleedTime;
|
||||
var Pawn instigator;
|
||||
var int count;
|
||||
};
|
||||
var PlayerReplicationInfo ownerPRI;
|
||||
var bool isBleeding;
|
||||
var int maxBleedCount;
|
||||
var BleedingState bleedState;
|
||||
var float bleedPeriod;
|
||||
var float bleedLevel;
|
||||
replication {
|
||||
reliable if (bNetDirty && Role == ROLE_Authority)
isBleeding, ownerPRI;
|
||||
}
|
||||
// Returns bleed damage, corresponding to given bleed level and damage scale.
|
||||
// Rand(7) should be used as a scale.
|
||||
// Separate function created to allow for lowest/highest damage value computing.
|
||||
function int calcBleedDamage(float level, int scale){
|
||||
return level * (3 + scale);
|
||||
}
|
||||
function Tick(float DeltaTime) {
|
||||
local PlayerController ownerCtrllr;
|
||||
local bool amAlive;
|
||||
local float bleedDamage;
|
||||
ownerCtrllr = PlayerController(Owner);
|
||||
amAlive = ownerCtrllr != none && ownerCtrllr.Pawn != none && ownerCtrllr.Pawn.Health > 0;
|
||||
if(amAlive && bleedState.count > 0) {
if(bleedState.nextBleedTime < Level.TimeSeconds) {
bleedState.count--;
bleedState.nextBleedTime+= bleedPeriod;
// Fix bleeding when stalker dies
bleedDamage = calcBleedDamage(bleedLevel, rand(7));
if(bleedDamage < 1.0)
stopBleeding();
if(bleedState.instigator != none)
ownerCtrllr.Pawn.TakeDamage(bleedDamage, bleedState.instigator, ownerCtrllr.Pawn.Location,
vect(0, 0, 0), class'NiceDamTypeStalkerBleed');
else
ownerCtrllr.Pawn.TakeDamage(bleedDamage, ownerCtrllr.Pawn, ownerCtrllr.Pawn.Location,
vect(0, 0, 0), class'NiceDamTypeStalkerBleed');
if (ownerCtrllr.Pawn.isA('KFPawn')) {
KFPawn(ownerCtrllr.Pawn).HealthToGive -= 2 * bleedLevel;
}
}
|
||||
} else {
isBleeding= false;
|
||||
}
|
||||
}
|
||||
function stopBleeding(){
|
||||
isBleeding = false;
|
||||
bleedState.count = 0;
|
||||
}
|
||||
function setBleeding(Pawn instigator, float effectStrenght) {
|
||||
// Can max possible damage do anything? If no, then don't even bother.
|
||||
if(calcBleedDamage(effectStrenght, 7) < 1.0)
return;
|
||||
bleedState.instigator = instigator;
|
||||
bleedState.count = maxBleedCount;
|
||||
bleedLevel = effectStrenght;
|
||||
if(!isBleeding){
bleedState.nextBleedTime = Level.TimeSeconds;
isBleeding = true;
|
||||
}
|
||||
}
|
||||
static function MeanReplicationInfo findSZri(PlayerReplicationInfo pri) {
|
||||
local MeanReplicationInfo repInfo;
|
||||
if(pri == none)
return none;
|
||||
foreach pri.DynamicActors(Class'MeanReplicationInfo', repInfo)
if(repInfo.ownerPRI == pri)
return repInfo;
|
||||
|
||||
return none;
|
||||
}
|
||||
defaultproperties
|
||||
{
maxBleedCount=7
bleedPeriod=1.500000
|
||||
}
|
||||
// Copy pasted from super zombies mutator with small alterations
|
||||
class MeanReplicationInfo extends ReplicationInfo;
|
||||
struct BleedingState {
|
||||
var float nextBleedTime;
|
||||
var Pawn instigator;
|
||||
var int count;
|
||||
};
|
||||
var PlayerReplicationInfo ownerPRI;
|
||||
var bool isBleeding;
|
||||
var int maxBleedCount;
|
||||
var BleedingState bleedState;
|
||||
var float bleedPeriod;
|
||||
var float bleedLevel;
|
||||
replication {
|
||||
reliable if (bNetDirty && Role == ROLE_Authority)
|
||||
isBleeding, ownerPRI;
|
||||
}
|
||||
// Returns bleed damage, corresponding to given bleed level and damage scale.
|
||||
// Rand(7) should be used as a scale.
|
||||
// Separate function created to allow for lowest/highest damage value computing.
|
||||
function int calcBleedDamage(float level, int scale){
|
||||
return level * (3 + scale);
|
||||
}
|
||||
function Tick(float DeltaTime) {
|
||||
local PlayerController ownerCtrllr;
|
||||
local bool amAlive;
|
||||
local float bleedDamage;
|
||||
ownerCtrllr = PlayerController(Owner);
|
||||
amAlive = ownerCtrllr != none && ownerCtrllr.Pawn != none && ownerCtrllr.Pawn.Health > 0;
|
||||
if(amAlive && bleedState.count > 0) {
|
||||
if(bleedState.nextBleedTime < Level.TimeSeconds) {
|
||||
bleedState.count--;
|
||||
bleedState.nextBleedTime+= bleedPeriod;
|
||||
// Fix bleeding when stalker dies
|
||||
bleedDamage = calcBleedDamage(bleedLevel, rand(7));
|
||||
if(bleedDamage < 1.0)
|
||||
stopBleeding();
|
||||
if(bleedState.instigator != none)
|
||||
ownerCtrllr.Pawn.TakeDamage(bleedDamage, bleedState.instigator, ownerCtrllr.Pawn.Location,
|
||||
vect(0, 0, 0), class'NiceDamTypeStalkerBleed');
|
||||
else
|
||||
ownerCtrllr.Pawn.TakeDamage(bleedDamage, ownerCtrllr.Pawn, ownerCtrllr.Pawn.Location,
|
||||
vect(0, 0, 0), class'NiceDamTypeStalkerBleed');
|
||||
if (ownerCtrllr.Pawn.isA('KFPawn')) {
|
||||
KFPawn(ownerCtrllr.Pawn).HealthToGive -= 2 * bleedLevel;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isBleeding= false;
|
||||
}
|
||||
}
|
||||
function stopBleeding(){
|
||||
isBleeding = false;
|
||||
bleedState.count = 0;
|
||||
}
|
||||
function setBleeding(Pawn instigator, float effectStrenght) {
|
||||
// Can max possible damage do anything? If no, then don't even bother.
|
||||
if(calcBleedDamage(effectStrenght, 7) < 1.0)
|
||||
return;
|
||||
bleedState.instigator = instigator;
|
||||
bleedState.count = maxBleedCount;
|
||||
bleedLevel = effectStrenght;
|
||||
if(!isBleeding){
|
||||
bleedState.nextBleedTime = Level.TimeSeconds;
|
||||
isBleeding = true;
|
||||
}
|
||||
}
|
||||
static function MeanReplicationInfo findSZri(PlayerReplicationInfo pri) {
|
||||
local MeanReplicationInfo repInfo;
|
||||
if(pri == none)
|
||||
return none;
|
||||
foreach pri.DynamicActors(Class'MeanReplicationInfo', repInfo)
|
||||
if(repInfo.ownerPRI == pri)
|
||||
return repInfo;
|
||||
|
||||
return none;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
maxBleedCount=7
|
||||
bleedPeriod=1.500000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,91 @@
|
|||
class MeanVoting extends ScrnVotingOptions;
|
||||
var NicePack Mut;
|
||||
function int GetGroupVoteIndex(PlayerController Sender, string Group, string Key, out string Value, out string VoteInfo)
|
||||
{
|
||||
local int ZedNumber;
|
||||
local int BoolValue;
|
||||
local bool bEnable;
|
||||
ZedNumber = Mut.ZedNumber(Key);
|
||||
BoolValue = TryStrToBool(Value);
|
||||
if(BoolValue == -1)
return VOTE_ILLEGAL;
|
||||
bEnable = (BoolValue == 1);
|
||||
if(Key ~= "ALL")
return 0;
|
||||
if (ZedNumber == -1)
return VOTE_UNKNOWN;
|
||||
if(bEnable == Mut.ZedDatabase[ZedNumber].bNeedsReplacement)
return VOTE_NOEFECT;
|
||||
else
return ZedNumber + 1;
|
||||
return VOTE_UNKNOWN;
|
||||
}
|
||||
function ApplyVoteValue(int VoteIndex, string VoteValue)
|
||||
{
|
||||
local int i;
|
||||
local int BoolValue;
|
||||
local bool bEnable;
|
||||
local bool bAffectsAll;
|
||||
bAffectsAll = false;
|
||||
if(VoteIndex == 0)
bAffectsAll = true;
|
||||
else
VoteIndex --;
|
||||
BoolValue = TryStrToBool(VoteValue);
|
||||
if ( BoolValue == -1 )
return;
|
||||
bEnable = (BoolValue == 1);
|
||||
if(!bAffectsAll)
Mut.ZedDatabase[VoteIndex].bNeedsReplacement = bEnable;
|
||||
else{
for(i = 0; i <= Mut.lastStandardZed;i ++)
Mut.ZedDatabase[i].bNeedsReplacement = bEnable;
|
||||
}
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "CLOT" || bAffectsAll)
Mut.bReplaceClot = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "CRAWLER" || bAffectsAll)
Mut.bReplaceCrawler = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "STALKER" || bAffectsAll)
Mut.bReplaceStalker = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "GOREFAST" || bAffectsAll)
Mut.bReplaceGorefast = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "BLOAT" || bAffectsAll)
Mut.bReplaceBloat = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "SIREN" || bAffectsAll)
Mut.bReplaceSiren = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "HUSK" || bAffectsAll)
Mut.bReplaceHusk = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "SCRAKE" || bAffectsAll)
Mut.bReplaceScrake = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "FLESHPOUND" || bAffectsAll)
Mut.bReplaceFleshpound = bEnable;
|
||||
Mut.SaveConfig();
|
||||
VotingHandler.BroadcastMessage(strRestartRequired);
|
||||
}
|
||||
function SendGroupHelp(PlayerController Sender, string Group)
|
||||
{
|
||||
local string s;
|
||||
local int i;
|
||||
local int ln;
|
||||
ln = 1;
|
||||
s $= "ALL";
|
||||
for ( i=0; i <= Mut.lastStandardZed; ++i ) {
if ( Mut.ZedDatabase[i].bNeedsReplacement )
s @= "%g";
else
s @= "%r";
s $= Caps(Mut.ZedDatabase[i].ZedName); if ( len(s) > 80 ) {
// move to new line
GroupInfo[ln++] = VotingHandler.ParseHelpLine(default.GroupInfo[1] @ s);
s = "";
} }
|
||||
GroupInfo[ln] = VotingHandler.ParseHelpLine(default.GroupInfo[1] @ s);
|
||||
super.SendGroupHelp(Sender, Group);
|
||||
}
|
||||
defaultproperties
|
||||
{
DefaultGroup="MEAN"
HelpInfo(0)="%pMEAN %y<zed_name> %gON%w|%rOFF %w Add|Remove mean zeds from the game. Type %bMVOTE MEAN HELP %w for more info."
GroupInfo(0)="%MEAN %y<zed_name> %gON%w|%rOFF %w Add or remove mean zeds from the game."
GroupInfo(1)="%wAvaliable mean zeds:"
|
||||
}
|
||||
class MeanVoting extends ScrnVotingOptions;
|
||||
var NicePack Mut;
|
||||
function int GetGroupVoteIndex(PlayerController Sender, string Group, string Key, out string Value, out string VoteInfo)
|
||||
{
|
||||
local int ZedNumber;
|
||||
local int BoolValue;
|
||||
local bool bEnable;
|
||||
ZedNumber = Mut.ZedNumber(Key);
|
||||
BoolValue = TryStrToBool(Value);
|
||||
if(BoolValue == -1)
|
||||
return VOTE_ILLEGAL;
|
||||
bEnable = (BoolValue == 1);
|
||||
if(Key ~= "ALL")
|
||||
return 0;
|
||||
if (ZedNumber == -1)
|
||||
return VOTE_UNKNOWN;
|
||||
if(bEnable == Mut.ZedDatabase[ZedNumber].bNeedsReplacement)
|
||||
return VOTE_NOEFECT;
|
||||
else
|
||||
return ZedNumber + 1;
|
||||
return VOTE_UNKNOWN;
|
||||
}
|
||||
function ApplyVoteValue(int VoteIndex, string VoteValue)
|
||||
{
|
||||
local int i;
|
||||
local int BoolValue;
|
||||
local bool bEnable;
|
||||
local bool bAffectsAll;
|
||||
bAffectsAll = false;
|
||||
if(VoteIndex == 0)
|
||||
bAffectsAll = true;
|
||||
else
|
||||
VoteIndex --;
|
||||
BoolValue = TryStrToBool(VoteValue);
|
||||
if ( BoolValue == -1 )
|
||||
return;
|
||||
bEnable = (BoolValue == 1);
|
||||
if(!bAffectsAll)
|
||||
Mut.ZedDatabase[VoteIndex].bNeedsReplacement = bEnable;
|
||||
else{
|
||||
for(i = 0; i <= Mut.lastStandardZed;i ++)
|
||||
Mut.ZedDatabase[i].bNeedsReplacement = bEnable;
|
||||
}
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "CLOT" || bAffectsAll)
|
||||
Mut.bReplaceClot = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "CRAWLER" || bAffectsAll)
|
||||
Mut.bReplaceCrawler = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "STALKER" || bAffectsAll)
|
||||
Mut.bReplaceStalker = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "GOREFAST" || bAffectsAll)
|
||||
Mut.bReplaceGorefast = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "BLOAT" || bAffectsAll)
|
||||
Mut.bReplaceBloat = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "SIREN" || bAffectsAll)
|
||||
Mut.bReplaceSiren = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "HUSK" || bAffectsAll)
|
||||
Mut.bReplaceHusk = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "SCRAKE" || bAffectsAll)
|
||||
Mut.bReplaceScrake = bEnable;
|
||||
if(Mut.ZedDatabase[VoteIndex].ZedName ~= "FLESHPOUND" || bAffectsAll)
|
||||
Mut.bReplaceFleshpound = bEnable;
|
||||
Mut.SaveConfig();
|
||||
VotingHandler.BroadcastMessage(strRestartRequired);
|
||||
}
|
||||
function SendGroupHelp(PlayerController Sender, string Group)
|
||||
{
|
||||
local string s;
|
||||
local int i;
|
||||
local int ln;
|
||||
ln = 1;
|
||||
s $= "ALL";
|
||||
for ( i=0; i <= Mut.lastStandardZed; ++i ) {
|
||||
if ( Mut.ZedDatabase[i].bNeedsReplacement )
|
||||
s @= "%g";
|
||||
else
|
||||
s @= "%r";
|
||||
s $= Caps(Mut.ZedDatabase[i].ZedName); if ( len(s) > 80 ) {
|
||||
// move to new line
|
||||
GroupInfo[ln++] = VotingHandler.ParseHelpLine(default.GroupInfo[1] @ s);
|
||||
s = "";
|
||||
} }
|
||||
GroupInfo[ln] = VotingHandler.ParseHelpLine(default.GroupInfo[1] @ s);
|
||||
super.SendGroupHelp(Sender, Group);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
DefaultGroup="MEAN"
|
||||
HelpInfo(0)="%pMEAN %y<zed_name> %gON%w|%rOFF %w Add|Remove mean zeds from the game. Type %bMVOTE MEAN HELP %w for more info."
|
||||
GroupInfo(0)="%MEAN %y<zed_name> %gON%w|%rOFF %w Add or remove mean zeds from the game."
|
||||
GroupInfo(1)="%wAvaliable mean zeds:"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,90 @@
|
|||
class NiceBossHPNeedle extends Decoration
|
||||
NotPlaceable;
|
||||
#exec obj load file="NewPatchSM.usx"
|
||||
simulated function DroppedNow()
|
||||
{
|
||||
SetCollision(True);
|
||||
SetPhysics(PHYS_Falling);
|
||||
bFixedRotationDir = True;
|
||||
RotationRate = RotRand(True);
|
||||
}
|
||||
simulated function HitWall( vector HitNormal, actor HitWall )
|
||||
{
|
||||
local rotator R;
|
||||
if( VSize(Velocity)<40 )
|
||||
{
SetPhysics(PHYS_none);
R.Roll = Rand(65536);
R.Yaw = Rand(65536);
SetRotation(R);
Return;
|
||||
}
|
||||
Velocity = MirrorVectorByNormal(Velocity,HitNormal)*0.75;
|
||||
if( HitWall!=none && HitWall.Physics!=PHYS_none )
Velocity+=HitWall.Velocity;
|
||||
}
|
||||
simulated function Landed( vector HitNormal )
|
||||
{
|
||||
HitWall(HitNormal,none);
|
||||
}
|
||||
function TakeDamage( int NDamage, Pawn instigatedBy, Vector hitlocation,
Vector momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
|
||||
if( Physics==PHYS_none )
|
||||
{
SetPhysics(PHYS_Falling);
bFixedRotationDir = True;
RotationRate = RotRand(True);
Velocity = vect(0,0,0);
|
||||
}
|
||||
Velocity+=momentum/10;
|
||||
}
|
||||
simulated function Destroyed();
|
||||
function Bump( actor Other );
|
||||
singular function PhysicsVolumeChange( PhysicsVolume NewVolume );
|
||||
// Overriden so it doesn't damage the patriarch when he drops a needle!
|
||||
singular function BaseChange()
|
||||
{
|
||||
if( Velocity.Z < -500 )
TakeDamage( (1-Velocity.Z/30),Instigator,Location,vect(0,0,0) , class'Crushed');
|
||||
if( base == none )
|
||||
{
if ( !bInterpolating && bPushable && (Physics == PHYS_none) )
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else if( Pawn(Base) != none )
|
||||
{
//Base.TakeDamage( (1-Velocity.Z/400)* mass/Base.Mass,Instigator,Location,0.5 * Velocity , class'Crushed');
Velocity.Z = 100;
if (FRand() < 0.5)
Velocity.X += 70;
else
Velocity.Y += 70;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else if( Decoration(Base)!=none && Velocity.Z<-500 )
|
||||
{
Base.TakeDamage((1 - Mass/Base.Mass * Velocity.Z/30), Instigator, Location, 0.2 * Velocity, class'Crushed');
Velocity.Z = 100;
if (FRand() < 0.5)
Velocity.X += 70;
else
Velocity.Y += 70;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else
instigator = none;
|
||||
}
|
||||
defaultproperties
|
||||
{
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'NewPatchSM.BossSyringe'
bStatic=False
RemoteRole=ROLE_None
LifeSpan=300.000000
CollisionRadius=4.000000
CollisionHeight=4.000000
bCollideWorld=True
bProjTarget=True
bBounce=True
|
||||
}
|
||||
class NiceBossHPNeedle extends Decoration
|
||||
NotPlaceable;
|
||||
#exec obj load file="NewPatchSM.usx"
|
||||
simulated function DroppedNow()
|
||||
{
|
||||
SetCollision(True);
|
||||
SetPhysics(PHYS_Falling);
|
||||
bFixedRotationDir = True;
|
||||
RotationRate = RotRand(True);
|
||||
}
|
||||
simulated function HitWall( vector HitNormal, actor HitWall )
|
||||
{
|
||||
local rotator R;
|
||||
if( VSize(Velocity)<40 )
|
||||
{
|
||||
SetPhysics(PHYS_none);
|
||||
R.Roll = Rand(65536);
|
||||
R.Yaw = Rand(65536);
|
||||
SetRotation(R);
|
||||
Return;
|
||||
}
|
||||
Velocity = MirrorVectorByNormal(Velocity,HitNormal)*0.75;
|
||||
if( HitWall!=none && HitWall.Physics!=PHYS_none )
|
||||
Velocity+=HitWall.Velocity;
|
||||
}
|
||||
simulated function Landed( vector HitNormal )
|
||||
{
|
||||
HitWall(HitNormal,none);
|
||||
}
|
||||
function TakeDamage( int NDamage, Pawn instigatedBy, Vector hitlocation,
|
||||
Vector momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
|
||||
if( Physics==PHYS_none )
|
||||
{
|
||||
SetPhysics(PHYS_Falling);
|
||||
bFixedRotationDir = True;
|
||||
RotationRate = RotRand(True);
|
||||
Velocity = vect(0,0,0);
|
||||
}
|
||||
Velocity+=momentum/10;
|
||||
}
|
||||
simulated function Destroyed();
|
||||
function Bump( actor Other );
|
||||
singular function PhysicsVolumeChange( PhysicsVolume NewVolume );
|
||||
// Overriden so it doesn't damage the patriarch when he drops a needle!
|
||||
singular function BaseChange()
|
||||
{
|
||||
if( Velocity.Z < -500 )
|
||||
TakeDamage( (1-Velocity.Z/30),Instigator,Location,vect(0,0,0) , class'Crushed');
|
||||
if( base == none )
|
||||
{
|
||||
if ( !bInterpolating && bPushable && (Physics == PHYS_none) )
|
||||
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else if( Pawn(Base) != none )
|
||||
{
|
||||
//Base.TakeDamage( (1-Velocity.Z/400)* mass/Base.Mass,Instigator,Location,0.5 * Velocity , class'Crushed');
|
||||
Velocity.Z = 100;
|
||||
if (FRand() < 0.5)
|
||||
Velocity.X += 70;
|
||||
else
|
||||
Velocity.Y += 70;
|
||||
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else if( Decoration(Base)!=none && Velocity.Z<-500 )
|
||||
{
|
||||
Base.TakeDamage((1 - Mass/Base.Mass * Velocity.Z/30), Instigator, Location, 0.2 * Velocity, class'Crushed');
|
||||
Velocity.Z = 100;
|
||||
if (FRand() < 0.5)
|
||||
Velocity.X += 70;
|
||||
else
|
||||
Velocity.Y += 70;
|
||||
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
else
|
||||
instigator = none;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
DrawType=DT_StaticMesh
|
||||
StaticMesh=StaticMesh'NewPatchSM.BossSyringe'
|
||||
bStatic=False
|
||||
RemoteRole=ROLE_None
|
||||
LifeSpan=300.000000
|
||||
CollisionRadius=4.000000
|
||||
CollisionHeight=4.000000
|
||||
bCollideWorld=True
|
||||
bProjTarget=True
|
||||
bBounce=True
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
class NiceBossLAWProj extends LAWProj;
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
// Difficulty Scaling
|
||||
if(Level.Game != none){
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
damage = default.damage * 1.3;
else
damage = default.damage * 1.0;
|
||||
}
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
defaultproperties
|
||||
{
ArmDistSquared=0.000000
Damage=200.000000
MyDamageType=Class'KFMod.DamTypeFrag'
|
||||
}
|
||||
class NiceBossLAWProj extends LAWProj;
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
// Difficulty Scaling
|
||||
if(Level.Game != none){
|
||||
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
|
||||
damage = default.damage * 1.3;
|
||||
else
|
||||
damage = default.damage * 1.0;
|
||||
}
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
ArmDistSquared=0.000000
|
||||
Damage=200.000000
|
||||
MyDamageType=Class'KFMod.DamTypeFrag'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
class NiceDamTypePoundCrushed extends NiceZedMeleeDamageType
|
||||
abstract;
|
||||
defaultproperties
|
||||
{
DeathString="%o was pounded by %k."
FemaleSuicide="%o was pounded."
MaleSuicide="%o was pounded."
bArmorStops=False
bLocationalHit=False
bThrowRagdoll=True
bExtraMomentumZ=True
GibPerterbation=1.000000
KDamageImpulse=7000.000000
KDeathVel=350.000000
KDeathUpKick=100.000000
HumanObliterationThreshhold=500
|
||||
}
|
||||
class NiceDamTypePoundCrushed extends NiceZedMeleeDamageType
|
||||
abstract;
|
||||
defaultproperties
|
||||
{
|
||||
DeathString="%o was pounded by %k."
|
||||
FemaleSuicide="%o was pounded."
|
||||
MaleSuicide="%o was pounded."
|
||||
bArmorStops=False
|
||||
bLocationalHit=False
|
||||
bThrowRagdoll=True
|
||||
bExtraMomentumZ=True
|
||||
GibPerterbation=1.000000
|
||||
KDamageImpulse=7000.000000
|
||||
KDeathVel=350.000000
|
||||
KDeathUpKick=100.000000
|
||||
HumanObliterationThreshhold=500
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,127 +1,292 @@
|
|||
class NiceHuskFireProjectile extends LAWProj;
|
||||
var Emitter FlameTrail;
|
||||
var xEmitter Trail;
|
||||
var class<DamageType> MyAdditionalDamageType;
|
||||
var float additionalDamagePart;
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
if ( !PhysicsVolume.bWaterVolume )
{
FlameTrail = Spawn(class'FlameThrowerFlameB',self);
Trail = Spawn(class'FlameThrowerFlame',self);
}
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
damage = default.damage * 1.3;
else
damage = default.damage * 1.0;
|
||||
}
|
||||
OrigLoc = Location;
|
||||
if( !bDud )
|
||||
{
Dir = vector(Rotation);
Velocity = speed * Dir;
|
||||
}
|
||||
super(ROBallisticProjectile).PostBeginPlay();
|
||||
}
|
||||
simulated function Explode(vector HitLocation, vector HitNormal)
|
||||
{
|
||||
local Controller C;
|
||||
local PlayerController LocalPlayer;
|
||||
local float ShakeScale;
|
||||
bHasExploded = True;
|
||||
// Don't explode if this is a dud
|
||||
if( bDud )
|
||||
{
Velocity = vect(0,0,0);
LifeSpan=1.0;
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
PlaySound(ExplosionSound,,2.0);
|
||||
if ( EffectIsRelevant(Location,false) )
|
||||
{
Spawn(class'KFMod.FlameImpact',,,HitLocation + HitNormal*20,rotator(HitNormal));
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
||||
}
|
||||
BlowUp(HitLocation);
|
||||
Destroy();
|
||||
// Shake nearby players screens
|
||||
LocalPlayer = Level.GetLocalPlayerController();
|
||||
if ( LocalPlayer != none )
|
||||
{
ShakeScale = GetShakeScale(Location, LocalPlayer.ViewTarget.Location);
if( ShakeScale > 0 )
{
LocalPlayer.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime);
}
|
||||
}
|
||||
for ( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if ( PlayerController(C) != none && C != LocalPlayer )
{
ShakeScale = GetShakeScale(Location, PlayerController(C).ViewTarget.Location);
if( ShakeScale > 0 )
{
C.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime);
}
}
|
||||
}
|
||||
}
|
||||
// Get the shake amount for when this projectile explodes
|
||||
simulated function float GetShakeScale(vector ViewLocation, vector EventLocation)
|
||||
{
|
||||
local float Dist;
|
||||
local float scale;
|
||||
Dist = VSize(ViewLocation - EventLocation);
|
||||
if (Dist < DamageRadius * 2.0 )
|
||||
{
scale = (DamageRadius*2.0 - Dist) / (DamageRadius*2.0);
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
/* HurtRadius()
|
||||
Hurt locally authoritative actors within the radius.
|
||||
Overriden so it doesn't attemt to damage the bullet whiz cylinder - TODO: maybe implement the same thing in the superclass - Ramm
|
||||
*/
|
||||
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
||||
{
|
||||
local actor Victims;
|
||||
local float damageScale, dist;
|
||||
local vector dirs;
|
||||
local int NumKilled;
|
||||
local KFMonster KFMonsterVictim;
|
||||
local Pawn P;
|
||||
local KFPawn KFP;
|
||||
local array<Pawn> CheckedPawns;
|
||||
local int i;
|
||||
local bool bAlreadyChecked;
|
||||
if ( bHurtEntry )
return;
|
||||
bHurtEntry = true;
|
||||
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
||||
{
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
if( (Victims != self) && (Victims != Instigator) &&(Hurtwall != Victims)
&& (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
&& ExtendedZCollision(Victims)==none && KFBulletWhipAttachment(Victims)==none )
{
dirs = Victims.Location - HitLocation;
dist = FMax(1,VSize(dirs));
dirs = dirs/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
if ( Instigator == none || Instigator.Controller == none )
Victims.SetDelayedDamageInstigatorController( InstigatorController );
if ( Victims == LastTouched )
LastTouched = none;
|
||||
P = Pawn(Victims);
|
||||
if( P != none )
{
for (i = 0; i < CheckedPawns.Length; i++)
{
if (CheckedPawns[i] == P)
{
bAlreadyChecked = true;
break;
}
}
|
||||
if( bAlreadyChecked )
{
bAlreadyChecked = false;
P = none;
continue;
}
|
||||
KFMonsterVictim = KFMonster(Victims);
|
||||
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
KFMonsterVictim = none;
}
|
||||
KFP = KFPawn(Victims);
|
||||
if( KFMonsterVictim != none )
{
damageScale *= KFMonsterVictim.GetExposureTo(HitLocation);
}
else if( KFP != none )
{
damageScale *= KFP.GetExposureTo(HitLocation);
}
|
||||
CheckedPawns[CheckedPawns.Length] = P;
|
||||
if ( damageScale <= 0)
{
P = none;
continue;
}
else
{
P = none;
}
}
|
||||
Victims.TakeDamage
(
damageScale * DamageAmount * (1.0 - additionalDamagePart),
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
(damageScale * Momentum * dirs),
DamageType
);
Victims.TakeDamage
(
damageScale * DamageAmount * additionalDamagePart,
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
Vect(0,0,0),
MyAdditionalDamageType
);
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
NumKilled++;
}
}
|
||||
}
|
||||
if ( (LastTouched != none) && (LastTouched != self) && (LastTouched != Instigator) &&
(LastTouched.Role == ROLE_Authority) && !LastTouched.IsA('FluidSurfaceInfo') )
|
||||
{
Victims = LastTouched;
LastTouched = none;
dirs = Victims.Location - HitLocation;
dist = FMax(1,VSize(dirs));
dirs = dirs/dist;
damageScale = FMax(Victims.CollisionRadius/(Victims.CollisionRadius + Victims.CollisionHeight),1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius));
if ( Instigator == none || Instigator.Controller == none )
Victims.SetDelayedDamageInstigatorController(InstigatorController);
|
||||
Victims.TakeDamage
(
damageScale * DamageAmount,
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
(damageScale * Momentum * dirs),
DamageType
);
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
}
|
||||
if( Role == ROLE_Authority )
|
||||
{
if( NumKilled >= 4 )
{
KFGameType(Level.Game).DramaticEvent(0.05);
}
else if( NumKilled >= 2 )
{
KFGameType(Level.Game).DramaticEvent(0.03);
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
//==============
|
||||
// Touching
|
||||
// Overridden to not touch the bulletwhip attachment
|
||||
simulated singular function Touch(Actor Other){
|
||||
if(Other == none || KFBulletWhipAttachment(Other) != none || Role < ROLE_Authority)
return;
|
||||
super.Touch(Other);
|
||||
}
|
||||
// Don't hit Zed extra collision cylinders
|
||||
// Do hit :3
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
|
||||
/*if(ExtendedZCollision(Other) != none){
return;*/
|
||||
super.ProcessTouch(Other, HitLocation);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( Trail != none )
|
||||
{
Trail.mRegen=False;
Trail.SetPhysics(PHYS_none);
Trail.GotoState('');
|
||||
}
|
||||
if ( FlameTrail != none )
|
||||
{
FlameTrail.Kill();
FlameTrail.SetPhysics(PHYS_none);
|
||||
}
|
||||
Super.Destroyed();
|
||||
}
|
||||
defaultproperties
|
||||
{
MyAdditionalDamageType=Class'KFMod.DamTypeLAW'
ExplosionSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_FireImpact'
ArmDistSquared=0.000000
Speed=1800.000000
MaxSpeed=2200.000000
Damage=25.000000
DamageRadius=150.000000
MyDamageType=Class'NicePack.NiceDamTypeFire'
ExplosionDecal=Class'KFMod.FlameThrowerBurnMark'
LightType=LT_Steady
LightHue=45
LightSaturation=169
LightBrightness=90.000000
LightRadius=16.000000
LightCone=16
StaticMesh=StaticMesh'EffectsSM.Weapons.Ger_Tracer'
bDynamicLight=True
bNetTemporary=False
AmbientSound=Sound'KF_BaseHusk.Fire.husk_fireball_loop'
DrawScale=2.000000
AmbientGlow=254
bUnlit=True
|
||||
}
|
||||
class NiceHuskFireProjectile extends LAWProj;
|
||||
var Emitter FlameTrail;
|
||||
var xEmitter Trail;
|
||||
var class<DamageType> MyAdditionalDamageType;
|
||||
var float additionalDamagePart;
|
||||
//-----------------------------------------------------------------------------
|
||||
// PostBeginPlay
|
||||
//-----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
|
||||
if ( !PhysicsVolume.bWaterVolume )
|
||||
{
|
||||
FlameTrail = Spawn(class'FlameThrowerFlameB',self);
|
||||
Trail = Spawn(class'FlameThrowerFlame',self);
|
||||
}
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
|
||||
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
|
||||
damage = default.damage * 1.3;
|
||||
else
|
||||
damage = default.damage * 1.0;
|
||||
}
|
||||
OrigLoc = Location;
|
||||
if( !bDud )
|
||||
{
|
||||
Dir = vector(Rotation);
|
||||
Velocity = speed * Dir;
|
||||
}
|
||||
super(ROBallisticProjectile).PostBeginPlay();
|
||||
}
|
||||
simulated function Explode(vector HitLocation, vector HitNormal)
|
||||
{
|
||||
local Controller C;
|
||||
local PlayerController LocalPlayer;
|
||||
local float ShakeScale;
|
||||
bHasExploded = True;
|
||||
// Don't explode if this is a dud
|
||||
if( bDud )
|
||||
{
|
||||
Velocity = vect(0,0,0);
|
||||
LifeSpan=1.0;
|
||||
SetPhysics(PHYS_Falling);
|
||||
}
|
||||
PlaySound(ExplosionSound,,2.0);
|
||||
if ( EffectIsRelevant(Location,false) )
|
||||
{
|
||||
Spawn(class'KFMod.FlameImpact',,,HitLocation + HitNormal*20,rotator(HitNormal));
|
||||
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
||||
}
|
||||
BlowUp(HitLocation);
|
||||
Destroy();
|
||||
// Shake nearby players screens
|
||||
LocalPlayer = Level.GetLocalPlayerController();
|
||||
if ( LocalPlayer != none )
|
||||
{
|
||||
ShakeScale = GetShakeScale(Location, LocalPlayer.ViewTarget.Location);
|
||||
if( ShakeScale > 0 )
|
||||
{
|
||||
LocalPlayer.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime);
|
||||
}
|
||||
}
|
||||
for ( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
|
||||
if ( PlayerController(C) != none && C != LocalPlayer )
|
||||
{
|
||||
ShakeScale = GetShakeScale(Location, PlayerController(C).ViewTarget.Location);
|
||||
if( ShakeScale > 0 )
|
||||
{
|
||||
C.ShakeView(RotMag * ShakeScale, RotRate, RotTime, OffsetMag * ShakeScale, OffsetRate, OffsetTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the shake amount for when this projectile explodes
|
||||
simulated function float GetShakeScale(vector ViewLocation, vector EventLocation)
|
||||
{
|
||||
local float Dist;
|
||||
local float scale;
|
||||
Dist = VSize(ViewLocation - EventLocation);
|
||||
if (Dist < DamageRadius * 2.0 )
|
||||
{
|
||||
scale = (DamageRadius*2.0 - Dist) / (DamageRadius*2.0);
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
/* HurtRadius()
|
||||
Hurt locally authoritative actors within the radius.
|
||||
Overriden so it doesn't attemt to damage the bullet whiz cylinder - TODO: maybe implement the same thing in the superclass - Ramm
|
||||
*/
|
||||
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
||||
{
|
||||
local actor Victims;
|
||||
local float damageScale, dist;
|
||||
local vector dirs;
|
||||
local int NumKilled;
|
||||
local KFMonster KFMonsterVictim;
|
||||
local Pawn P;
|
||||
local KFPawn KFP;
|
||||
local array<Pawn> CheckedPawns;
|
||||
local int i;
|
||||
local bool bAlreadyChecked;
|
||||
if ( bHurtEntry )
|
||||
return;
|
||||
bHurtEntry = true;
|
||||
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
||||
{
|
||||
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
|
||||
if( (Victims != self) && (Victims != Instigator) &&(Hurtwall != Victims)
|
||||
&& (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
|
||||
&& ExtendedZCollision(Victims)==none && KFBulletWhipAttachment(Victims)==none )
|
||||
{
|
||||
dirs = Victims.Location - HitLocation;
|
||||
dist = FMax(1,VSize(dirs));
|
||||
dirs = dirs/dist;
|
||||
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
|
||||
if ( Instigator == none || Instigator.Controller == none )
|
||||
Victims.SetDelayedDamageInstigatorController( InstigatorController );
|
||||
if ( Victims == LastTouched )
|
||||
LastTouched = none;
|
||||
|
||||
P = Pawn(Victims);
|
||||
|
||||
if( P != none )
|
||||
{
|
||||
for (i = 0; i < CheckedPawns.Length; i++)
|
||||
{
|
||||
if (CheckedPawns[i] == P)
|
||||
{
|
||||
bAlreadyChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bAlreadyChecked )
|
||||
{
|
||||
bAlreadyChecked = false;
|
||||
P = none;
|
||||
continue;
|
||||
}
|
||||
|
||||
KFMonsterVictim = KFMonster(Victims);
|
||||
|
||||
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
||||
{
|
||||
KFMonsterVictim = none;
|
||||
}
|
||||
|
||||
KFP = KFPawn(Victims);
|
||||
|
||||
if( KFMonsterVictim != none )
|
||||
{
|
||||
damageScale *= KFMonsterVictim.GetExposureTo(HitLocation);
|
||||
}
|
||||
else if( KFP != none )
|
||||
{
|
||||
damageScale *= KFP.GetExposureTo(HitLocation);
|
||||
}
|
||||
|
||||
CheckedPawns[CheckedPawns.Length] = P;
|
||||
|
||||
if ( damageScale <= 0)
|
||||
{
|
||||
P = none;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
P = none;
|
||||
}
|
||||
}
|
||||
|
||||
Victims.TakeDamage
|
||||
(
|
||||
damageScale * DamageAmount * (1.0 - additionalDamagePart),
|
||||
Instigator,
|
||||
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
|
||||
(damageScale * Momentum * dirs),
|
||||
DamageType
|
||||
);
|
||||
Victims.TakeDamage
|
||||
(
|
||||
damageScale * DamageAmount * additionalDamagePart,
|
||||
Instigator,
|
||||
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
|
||||
Vect(0,0,0),
|
||||
MyAdditionalDamageType
|
||||
);
|
||||
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
|
||||
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
|
||||
if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
||||
{
|
||||
NumKilled++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( (LastTouched != none) && (LastTouched != self) && (LastTouched != Instigator) &&
|
||||
(LastTouched.Role == ROLE_Authority) && !LastTouched.IsA('FluidSurfaceInfo') )
|
||||
{
|
||||
Victims = LastTouched;
|
||||
LastTouched = none;
|
||||
dirs = Victims.Location - HitLocation;
|
||||
dist = FMax(1,VSize(dirs));
|
||||
dirs = dirs/dist;
|
||||
damageScale = FMax(Victims.CollisionRadius/(Victims.CollisionRadius + Victims.CollisionHeight),1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius));
|
||||
if ( Instigator == none || Instigator.Controller == none )
|
||||
Victims.SetDelayedDamageInstigatorController(InstigatorController);
|
||||
|
||||
Victims.TakeDamage
|
||||
(
|
||||
damageScale * DamageAmount,
|
||||
Instigator,
|
||||
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dirs,
|
||||
(damageScale * Momentum * dirs),
|
||||
DamageType
|
||||
);
|
||||
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
|
||||
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
}
|
||||
if( Role == ROLE_Authority )
|
||||
{
|
||||
if( NumKilled >= 4 )
|
||||
{
|
||||
KFGameType(Level.Game).DramaticEvent(0.05);
|
||||
}
|
||||
else if( NumKilled >= 2 )
|
||||
{
|
||||
KFGameType(Level.Game).DramaticEvent(0.03);
|
||||
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
//==============
|
||||
// Touching
|
||||
// Overridden to not touch the bulletwhip attachment
|
||||
simulated singular function Touch(Actor Other){
|
||||
if(Other == none || KFBulletWhipAttachment(Other) != none || Role < ROLE_Authority)
|
||||
return;
|
||||
super.Touch(Other);
|
||||
}
|
||||
// Don't hit Zed extra collision cylinders
|
||||
// Do hit :3
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
|
||||
/*if(ExtendedZCollision(Other) != none){
|
||||
return;*/
|
||||
super.ProcessTouch(Other, HitLocation);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( Trail != none )
|
||||
{
|
||||
Trail.mRegen=False;
|
||||
Trail.SetPhysics(PHYS_none);
|
||||
Trail.GotoState('');
|
||||
}
|
||||
if ( FlameTrail != none )
|
||||
{
|
||||
FlameTrail.Kill();
|
||||
FlameTrail.SetPhysics(PHYS_none);
|
||||
}
|
||||
Super.Destroyed();
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
MyAdditionalDamageType=Class'KFMod.DamTypeLAW'
|
||||
ExplosionSound=SoundGroup'KF_EnemiesFinalSnd.Husk.Husk_FireImpact'
|
||||
ArmDistSquared=0.000000
|
||||
Speed=1800.000000
|
||||
MaxSpeed=2200.000000
|
||||
Damage=25.000000
|
||||
DamageRadius=150.000000
|
||||
MyDamageType=Class'NicePack.NiceDamTypeFire'
|
||||
ExplosionDecal=Class'KFMod.FlameThrowerBurnMark'
|
||||
LightType=LT_Steady
|
||||
LightHue=45
|
||||
LightSaturation=169
|
||||
LightBrightness=90.000000
|
||||
LightRadius=16.000000
|
||||
LightCone=16
|
||||
StaticMesh=StaticMesh'EffectsSM.Weapons.Ger_Tracer'
|
||||
bDynamicLight=True
|
||||
bNetTemporary=False
|
||||
AmbientSound=Sound'KF_BaseHusk.Fire.husk_fireball_loop'
|
||||
DrawScale=2.000000
|
||||
AmbientGlow=254
|
||||
bUnlit=True
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class NiceSeveredArmSick extends SeveredArm;
|
||||
defaultproperties
|
||||
{
StaticMesh=StaticMesh'NicePackSM.MonsterSick.Arm'
|
||||
}
|
||||
class NiceSeveredArmSick extends SeveredArm;
|
||||
defaultproperties
|
||||
{
|
||||
StaticMesh=StaticMesh'NicePackSM.MonsterSick.Arm'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
//=============================================================================
|
||||
// SeveredHeadClot
|
||||
//=============================================================================
|
||||
// Detached head gib class for the clot
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceSeveredHeadSick extends SeveredHead;
|
||||
defaultproperties
|
||||
{
StaticMesh=StaticMesh'NicePackSM.MonsterSick.head'
|
||||
}
|
||||
//=============================================================================
|
||||
// SeveredHeadClot
|
||||
//=============================================================================
|
||||
// Detached head gib class for the clot
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceSeveredHeadSick extends SeveredHead;
|
||||
defaultproperties
|
||||
{
|
||||
StaticMesh=StaticMesh'NicePackSM.MonsterSick.head'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class NiceSeveredLegSick extends SeveredLeg;
|
||||
defaultproperties
|
||||
{
StaticMesh=StaticMesh'NicePackSM.MonsterSick.Leg'
|
||||
}
|
||||
class NiceSeveredLegSick extends SeveredLeg;
|
||||
defaultproperties
|
||||
{
|
||||
StaticMesh=StaticMesh'NicePackSM.MonsterSick.Leg'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,86 +1,220 @@
|
|||
// The Nice, nasty barf we'll be using for the Bloat's ranged attack.
|
||||
class NiceSickVomit extends BioGlob;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
SetOwner(none);
|
||||
if (Role == ROLE_Authority)
|
||||
{
Velocity = Vector(Rotation) * Speed;
Velocity.Z += TossZ;
|
||||
}
|
||||
if (Role == ROLE_Authority)
Rand3 = Rand(3);
|
||||
if ( (Level.NetMode != NM_DedicatedServer) && ((Level.DetailMode == DM_Low) || Level.bDropDetail) )
|
||||
{
bDynamicLight = false;
LightType = LT_none;
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
BaseDamage = Max((DifficultyDamageModifer() * BaseDamage),1);
Damage = Max((DifficultyDamageModifer() * Damage),1);
|
||||
}
|
||||
}
|
||||
// Scales the damage this Zed deals by the difficulty level
|
||||
function float DifficultyDamageModifer()
|
||||
{
|
||||
local float AdjustedDamageModifier;
|
||||
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
damage = default.damage * 2.5;
|
||||
else
damage = default.damage * 1.5;
|
||||
return AdjustedDamageModifier;
|
||||
}
|
||||
state OnGround
|
||||
{
|
||||
simulated function BeginState()
|
||||
{
SetTimer(RestTime, false);
BlowUp(Location);
|
||||
}
|
||||
simulated function Timer()
|
||||
{
if (bDrip)
{
bDrip = false;
SetCollisionSize(default.CollisionHeight, default.CollisionRadius);
Velocity = PhysicsVolume.Gravity * 0.2;
SetPhysics(PHYS_Falling);
bCollideWorld = true;
bCheckedsurface = false;
bProjTarget = false;
GotoState('Flying');
}
else BlowUp(Location);
|
||||
}
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
if ( Other != none )
BlowUp(Location);
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
if (DamageType.default.bDetonatesGoop)
{
bDrip = false;
SetTimer(0.1, false);
}
|
||||
}
|
||||
simulated function AnimEnd(int Channel)
|
||||
{
local float DotProduct;
|
||||
if (!bCheckedSurface)
{
DotProduct = SurfaceNormal dot Vect(0,0,-1);
if (DotProduct > 0.7)
{
bDrip = true;
SetTimer(DripTime, false);
if (bOnMover)
BlowUp(Location);
}
else if (DotProduct > -0.5)
{
if (bOnMover)
BlowUp(Location);
}
bCheckedSurface = true;
}
|
||||
}
|
||||
simulated function MergeWithGlob(int AdditionalGoopLevel)
|
||||
{
local int NewGoopLevel, ExtraSplash;
NewGoopLevel = AdditionalGoopLevel + GoopLevel;
if (NewGoopLevel > MaxGoopLevel)
{
Rand3 = (Rand3 + 1) % 3;
ExtraSplash = Rand3;
if (Role == ROLE_Authority)
SplashGlobs(NewGoopLevel - MaxGoopLevel + ExtraSplash);
NewGoopLevel = MaxGoopLevel - ExtraSplash;
}
SetGoopLevel(NewGoopLevel);
SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0);
PlaySound(ImpactSound, SLOT_Misc);
bCheckedSurface = false;
SetTimer(RestTime, false);
|
||||
}
|
||||
}
|
||||
singular function SplashGlobs(int NumGloblings)
|
||||
{
|
||||
local int g;
|
||||
local NiceSickVomit NewGlob;
|
||||
local Vector VNorm;
|
||||
for (g=0; g<NumGloblings; g++)
|
||||
{
NewGlob = Spawn(Class, self,, Location+GoopVolume*(CollisionHeight+4.0)*SurfaceNormal);
if (NewGlob != none)
{
NewGlob.Velocity = (GloblingSpeed + FRand()*150.0) * (SurfaceNormal + VRand()*0.8);
if (Physics == PHYS_Falling)
{
VNorm = (Velocity dot SurfaceNormal) * SurfaceNormal;
NewGlob.Velocity += (-VNorm + (Velocity - VNorm)) * 0.1;
}
NewGlob.InstigatorController = InstigatorController;
}
//else log("unable to spawn globling");
|
||||
}
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( !bNoFX && EffectIsRelevant(Location,false) )
|
||||
{
//Spawn(class'xEffects.GoopSmoke');
Spawn(class'KFmod.VomGroundSplash');
|
||||
}
|
||||
if ( Fear != none )
Fear.Destroy();
|
||||
if (Trail != none)
Trail.Destroy();
|
||||
//Super.Destroyed();
|
||||
}
|
||||
|
||||
auto state Flying
|
||||
{
|
||||
simulated function Landed( Vector HitNormal )
|
||||
{
local Rotator NewRot;
local int CoreGoopLevel;
|
||||
if ( Level.NetMode != NM_DedicatedServer )
{
PlaySound(ImpactSound, SLOT_Misc);
// explosion effects
}
|
||||
SurfaceNormal = HitNormal;
|
||||
// spawn globlings
CoreGoopLevel = Rand3 + MaxGoopLevel - 3;
if (GoopLevel > CoreGoopLevel)
{
if (Role == ROLE_Authority)
SplashGlobs(GoopLevel - CoreGoopLevel);
SetGoopLevel(CoreGoopLevel);
}
spawn(class'KFMod.VomitDecal',,,, rotator(-HitNormal));
|
||||
bCollideWorld = false;
SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0);
bProjTarget = true;
|
||||
NewRot = Rotator(HitNormal);
NewRot.Roll += 32768;
SetRotation(NewRot);
SetPhysics(PHYS_none);
bCheckedsurface = false;
Fear = Spawn(class'AvoidMarker');
GotoState('OnGround');
|
||||
}
|
||||
simulated function HitWall( Vector HitNormal, Actor Wall )
|
||||
{
Landed(HitNormal);
if ( !Wall.bStatic && !Wall.bWorldGeometry )
{
bOnMover = true;
SetBase(Wall);
if (Base == none)
BlowUp(Location);
}
|
||||
}
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
if( ExtendedZCollision(Other)!=none )
Return;
if (Other != Instigator && (Other.IsA('Pawn') || Other.IsA('DestroyableObjective') || Other.bProjTarget))
HurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation );
else if ( Other != Instigator && Other.bBlockActors )
HitWall( Normal(HitLocation-Location), Other );
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
BaseDamage=4
TouchDetonationDelay=0.000000
Speed=600.000000
Damage=5.000000
MomentumTransfer=3500.000000
MyDamageType=Class'KFMod.DamTypeVomit'
ImpactSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_AcidSplash'
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'kf_gore_trip_sm.puke.puke_chunk'
bDynamicLight=False
LifeSpan=8.000000
Skins(0)=Combiner'kf_fx_trip_t.Gore.intestines_cmb'
bUseCollisionStaticMesh=False
bBlockHitPointTraces=False
|
||||
}
|
||||
// The Nice, nasty barf we'll be using for the Bloat's ranged attack.
|
||||
class NiceSickVomit extends BioGlob;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
SetOwner(none);
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
Velocity = Vector(Rotation) * Speed;
|
||||
Velocity.Z += TossZ;
|
||||
}
|
||||
if (Role == ROLE_Authority)
|
||||
Rand3 = Rand(3);
|
||||
if ( (Level.NetMode != NM_DedicatedServer) && ((Level.DetailMode == DM_Low) || Level.bDropDetail) )
|
||||
{
|
||||
bDynamicLight = false;
|
||||
LightType = LT_none;
|
||||
}
|
||||
// Difficulty Scaling
|
||||
if (Level.Game != none)
|
||||
{
|
||||
BaseDamage = Max((DifficultyDamageModifer() * BaseDamage),1);
|
||||
Damage = Max((DifficultyDamageModifer() * Damage),1);
|
||||
}
|
||||
}
|
||||
// Scales the damage this Zed deals by the difficulty level
|
||||
function float DifficultyDamageModifer()
|
||||
{
|
||||
local float AdjustedDamageModifier;
|
||||
if(Level.Game.GameDifficulty >= 5.0) // Hell on Earth & Suicidal
|
||||
damage = default.damage * 2.5;
|
||||
else
|
||||
damage = default.damage * 1.5;
|
||||
return AdjustedDamageModifier;
|
||||
}
|
||||
state OnGround
|
||||
{
|
||||
simulated function BeginState()
|
||||
{
|
||||
SetTimer(RestTime, false);
|
||||
BlowUp(Location);
|
||||
}
|
||||
simulated function Timer()
|
||||
{
|
||||
if (bDrip)
|
||||
{
|
||||
bDrip = false;
|
||||
SetCollisionSize(default.CollisionHeight, default.CollisionRadius);
|
||||
Velocity = PhysicsVolume.Gravity * 0.2;
|
||||
SetPhysics(PHYS_Falling);
|
||||
bCollideWorld = true;
|
||||
bCheckedsurface = false;
|
||||
bProjTarget = false;
|
||||
GotoState('Flying');
|
||||
}
|
||||
else BlowUp(Location);
|
||||
}
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
|
||||
if ( Other != none )
|
||||
BlowUp(Location);
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
|
||||
{
|
||||
if (DamageType.default.bDetonatesGoop)
|
||||
{
|
||||
bDrip = false;
|
||||
SetTimer(0.1, false);
|
||||
}
|
||||
}
|
||||
simulated function AnimEnd(int Channel)
|
||||
{
|
||||
local float DotProduct;
|
||||
|
||||
if (!bCheckedSurface)
|
||||
{
|
||||
DotProduct = SurfaceNormal dot Vect(0,0,-1);
|
||||
if (DotProduct > 0.7)
|
||||
{
|
||||
bDrip = true;
|
||||
SetTimer(DripTime, false);
|
||||
if (bOnMover)
|
||||
BlowUp(Location);
|
||||
}
|
||||
else if (DotProduct > -0.5)
|
||||
{
|
||||
if (bOnMover)
|
||||
BlowUp(Location);
|
||||
}
|
||||
bCheckedSurface = true;
|
||||
}
|
||||
}
|
||||
simulated function MergeWithGlob(int AdditionalGoopLevel)
|
||||
{
|
||||
local int NewGoopLevel, ExtraSplash;
|
||||
NewGoopLevel = AdditionalGoopLevel + GoopLevel;
|
||||
if (NewGoopLevel > MaxGoopLevel)
|
||||
{
|
||||
Rand3 = (Rand3 + 1) % 3;
|
||||
ExtraSplash = Rand3;
|
||||
if (Role == ROLE_Authority)
|
||||
SplashGlobs(NewGoopLevel - MaxGoopLevel + ExtraSplash);
|
||||
NewGoopLevel = MaxGoopLevel - ExtraSplash;
|
||||
}
|
||||
SetGoopLevel(NewGoopLevel);
|
||||
SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0);
|
||||
PlaySound(ImpactSound, SLOT_Misc);
|
||||
bCheckedSurface = false;
|
||||
SetTimer(RestTime, false);
|
||||
}
|
||||
}
|
||||
singular function SplashGlobs(int NumGloblings)
|
||||
{
|
||||
local int g;
|
||||
local NiceSickVomit NewGlob;
|
||||
local Vector VNorm;
|
||||
for (g=0; g<NumGloblings; g++)
|
||||
{
|
||||
NewGlob = Spawn(Class, self,, Location+GoopVolume*(CollisionHeight+4.0)*SurfaceNormal);
|
||||
if (NewGlob != none)
|
||||
{
|
||||
NewGlob.Velocity = (GloblingSpeed + FRand()*150.0) * (SurfaceNormal + VRand()*0.8);
|
||||
if (Physics == PHYS_Falling)
|
||||
{
|
||||
VNorm = (Velocity dot SurfaceNormal) * SurfaceNormal;
|
||||
NewGlob.Velocity += (-VNorm + (Velocity - VNorm)) * 0.1;
|
||||
}
|
||||
NewGlob.InstigatorController = InstigatorController;
|
||||
}
|
||||
//else log("unable to spawn globling");
|
||||
}
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( !bNoFX && EffectIsRelevant(Location,false) )
|
||||
{
|
||||
//Spawn(class'xEffects.GoopSmoke');
|
||||
Spawn(class'KFmod.VomGroundSplash');
|
||||
}
|
||||
if ( Fear != none )
|
||||
Fear.Destroy();
|
||||
if (Trail != none)
|
||||
Trail.Destroy();
|
||||
//Super.Destroyed();
|
||||
}
|
||||
|
||||
auto state Flying
|
||||
{
|
||||
simulated function Landed( Vector HitNormal )
|
||||
{
|
||||
local Rotator NewRot;
|
||||
local int CoreGoopLevel;
|
||||
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
|
||||
PlaySound(ImpactSound, SLOT_Misc);
|
||||
// explosion effects
|
||||
}
|
||||
|
||||
SurfaceNormal = HitNormal;
|
||||
|
||||
// spawn globlings
|
||||
CoreGoopLevel = Rand3 + MaxGoopLevel - 3;
|
||||
if (GoopLevel > CoreGoopLevel)
|
||||
{
|
||||
if (Role == ROLE_Authority)
|
||||
SplashGlobs(GoopLevel - CoreGoopLevel);
|
||||
SetGoopLevel(CoreGoopLevel);
|
||||
}
|
||||
spawn(class'KFMod.VomitDecal',,,, rotator(-HitNormal));
|
||||
|
||||
bCollideWorld = false;
|
||||
SetCollisionSize(GoopVolume*10.0, GoopVolume*10.0);
|
||||
bProjTarget = true;
|
||||
|
||||
NewRot = Rotator(HitNormal);
|
||||
NewRot.Roll += 32768;
|
||||
SetRotation(NewRot);
|
||||
SetPhysics(PHYS_none);
|
||||
bCheckedsurface = false;
|
||||
Fear = Spawn(class'AvoidMarker');
|
||||
GotoState('OnGround');
|
||||
}
|
||||
simulated function HitWall( Vector HitNormal, Actor Wall )
|
||||
{
|
||||
Landed(HitNormal);
|
||||
if ( !Wall.bStatic && !Wall.bWorldGeometry )
|
||||
{
|
||||
bOnMover = true;
|
||||
SetBase(Wall);
|
||||
if (Base == none)
|
||||
BlowUp(Location);
|
||||
}
|
||||
}
|
||||
simulated function ProcessTouch(Actor Other, Vector HitLocation)
|
||||
{
|
||||
if( ExtendedZCollision(Other)!=none )
|
||||
Return;
|
||||
if (Other != Instigator && (Other.IsA('Pawn') || Other.IsA('DestroyableObjective') || Other.bProjTarget))
|
||||
HurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation );
|
||||
else if ( Other != Instigator && Other.bBlockActors )
|
||||
HitWall( Normal(HitLocation-Location), Other );
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
BaseDamage=4
|
||||
TouchDetonationDelay=0.000000
|
||||
Speed=600.000000
|
||||
Damage=5.000000
|
||||
MomentumTransfer=3500.000000
|
||||
MyDamageType=Class'KFMod.DamTypeVomit'
|
||||
ImpactSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_AcidSplash'
|
||||
DrawType=DT_StaticMesh
|
||||
StaticMesh=StaticMesh'kf_gore_trip_sm.puke.puke_chunk'
|
||||
bDynamicLight=False
|
||||
LifeSpan=8.000000
|
||||
Skins(0)=Combiner'kf_fx_trip_t.Gore.intestines_cmb'
|
||||
bUseCollisionStaticMesh=False
|
||||
bBlockHitPointTraces=False
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,148 @@
|
|||
// used for Tesla Husk self-destruct explosion on decapitation
|
||||
class NiceTeslaEMPNade extends NiceNade;
|
||||
var() class<Emitter> ExplosionEffect;
|
||||
function Timer(){
|
||||
if(bHidden)
Destroy();
|
||||
else if(Instigator != none && Instigator.Health > 0)
Explode(Location, vect(0,0,1));
|
||||
else
Disintegrate(Location, vect(0,0,1));
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex){}
|
||||
simulated function Explode(vector HitLocation, vector HitNormal)
|
||||
{
|
||||
local PlayerController LocalPlayer;
|
||||
bHasExploded = True;
|
||||
BlowUp(HitLocation);
|
||||
if(ExplodeSounds.length > 0)
PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],, 2.0);
|
||||
if(EffectIsRelevant(Location, false)){
Spawn(ExplosionEffect,,, HitLocation, rotator(vect(0,0,1)));
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
||||
}
|
||||
// Shake nearby players screens
|
||||
LocalPlayer = Level.GetLocalPlayerController();
|
||||
if((LocalPlayer != none) && (VSize(Location - LocalPlayer.ViewTarget.Location) < (DamageRadius * 1.5)))
LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
||||
if(Instigator != none){
// blow up the instigator
Instigator.TakeDamage(1000000, Instigator, Instigator.Location, vect(0,0,1), MyDamageType);
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
||||
{
|
||||
local actor Victims;
|
||||
local float damageScale, dist;
|
||||
local vector dir;
|
||||
local KFMonster KFMonsterVictim;
|
||||
local Pawn P;
|
||||
local KFPawn KFP;
|
||||
local array<Pawn> CheckedPawns;
|
||||
local int i;
|
||||
local bool bAlreadyChecked;
|
||||
|
||||
if ( bHurtEntry )
return;
|
||||
bHurtEntry = true;
|
||||
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
||||
{
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
&& ExtendedZCollision(Victims)==none )
{
if( (Instigator==none || Instigator.Health<=0) && KFPawn(Victims)!=none )
Continue;
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
|
||||
if ( Instigator == none || Instigator.Controller == none )
{
Victims.SetDelayedDamageInstigatorController( InstigatorController );
}
|
||||
P = Pawn(Victims);
|
||||
if( P != none )
{
for (i = 0; i < CheckedPawns.Length; i++)
{
if (CheckedPawns[i] == P)
{
bAlreadyChecked = true;
break;
}
}
|
||||
if( bAlreadyChecked )
{
bAlreadyChecked = false;
P = none;
continue;
}
|
||||
KFMonsterVictim = KFMonster(Victims);
|
||||
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
{
KFMonsterVictim = none;
}
|
||||
KFP = KFPawn(Victims);
|
||||
if( KFMonsterVictim != none )
{
// 10x more damage zeds
damageScale *= 10.0 * KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
if ( ZombieFleshpound(KFMonsterVictim) != none )
damageScale *= 2.0; // compensate 50% dmg.res.
}
else if( KFP != none )
{
damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
}
|
||||
CheckedPawns[CheckedPawns.Length] = P;
|
||||
if ( damageScale <= 0)
{
P = none;
continue;
}
else
{
//Victims = P;
P = none;
}
}
|
||||
Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius)
* dir,(damageScale * Momentum * dir),DamageType);
|
||||
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
{
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
defaultproperties
|
||||
{
ExplosionEffect=Class'KFMod.ZEDMKIISecondaryProjectileExplosion'
ShrapnelClass=None
ExplodeSounds(0)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
ExplodeSounds(1)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
ExplodeSounds(2)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
Speed=0.000000
Damage=50.000000
DamageRadius=400.000000
MyDamageType=Class'ScrnZedPack.DamTypeEMP'
DrawType=DT_None
bCollideActors=False
bBlockZeroExtentTraces=False
bBlockNonZeroExtentTraces=False
|
||||
}
|
||||
// used for Tesla Husk self-destruct explosion on decapitation
|
||||
class NiceTeslaEMPNade extends NiceNade;
|
||||
var() class<Emitter> ExplosionEffect;
|
||||
function Timer(){
|
||||
if(bHidden)
|
||||
Destroy();
|
||||
else if(Instigator != none && Instigator.Health > 0)
|
||||
Explode(Location, vect(0,0,1));
|
||||
else
|
||||
Disintegrate(Location, vect(0,0,1));
|
||||
}
|
||||
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex){}
|
||||
simulated function Explode(vector HitLocation, vector HitNormal)
|
||||
{
|
||||
local PlayerController LocalPlayer;
|
||||
bHasExploded = True;
|
||||
BlowUp(HitLocation);
|
||||
if(ExplodeSounds.length > 0)
|
||||
PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],, 2.0);
|
||||
if(EffectIsRelevant(Location, false)){
|
||||
Spawn(ExplosionEffect,,, HitLocation, rotator(vect(0,0,1)));
|
||||
Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
|
||||
}
|
||||
// Shake nearby players screens
|
||||
LocalPlayer = Level.GetLocalPlayerController();
|
||||
if((LocalPlayer != none) && (VSize(Location - LocalPlayer.ViewTarget.Location) < (DamageRadius * 1.5)))
|
||||
LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime);
|
||||
if(Instigator != none){
|
||||
// blow up the instigator
|
||||
Instigator.TakeDamage(1000000, Instigator, Instigator.Location, vect(0,0,1), MyDamageType);
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
simulated function HurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
|
||||
{
|
||||
local actor Victims;
|
||||
local float damageScale, dist;
|
||||
local vector dir;
|
||||
local KFMonster KFMonsterVictim;
|
||||
local Pawn P;
|
||||
local KFPawn KFP;
|
||||
local array<Pawn> CheckedPawns;
|
||||
local int i;
|
||||
local bool bAlreadyChecked;
|
||||
|
||||
if ( bHurtEntry )
|
||||
return;
|
||||
bHurtEntry = true;
|
||||
foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation)
|
||||
{
|
||||
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
|
||||
if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo')
|
||||
&& ExtendedZCollision(Victims)==none )
|
||||
{
|
||||
if( (Instigator==none || Instigator.Health<=0) && KFPawn(Victims)!=none )
|
||||
Continue;
|
||||
dir = Victims.Location - HitLocation;
|
||||
dist = FMax(1,VSize(dir));
|
||||
dir = dir/dist;
|
||||
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
|
||||
|
||||
if ( Instigator == none || Instigator.Controller == none )
|
||||
{
|
||||
Victims.SetDelayedDamageInstigatorController( InstigatorController );
|
||||
}
|
||||
|
||||
P = Pawn(Victims);
|
||||
|
||||
if( P != none )
|
||||
{
|
||||
for (i = 0; i < CheckedPawns.Length; i++)
|
||||
{
|
||||
if (CheckedPawns[i] == P)
|
||||
{
|
||||
bAlreadyChecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bAlreadyChecked )
|
||||
{
|
||||
bAlreadyChecked = false;
|
||||
P = none;
|
||||
continue;
|
||||
}
|
||||
|
||||
KFMonsterVictim = KFMonster(Victims);
|
||||
|
||||
if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 )
|
||||
{
|
||||
KFMonsterVictim = none;
|
||||
}
|
||||
|
||||
KFP = KFPawn(Victims);
|
||||
|
||||
if( KFMonsterVictim != none )
|
||||
{
|
||||
// 10x more damage zeds
|
||||
damageScale *= 10.0 * KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
|
||||
if ( ZombieFleshpound(KFMonsterVictim) != none )
|
||||
damageScale *= 2.0; // compensate 50% dmg.res.
|
||||
}
|
||||
else if( KFP != none )
|
||||
{
|
||||
damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity));
|
||||
}
|
||||
|
||||
CheckedPawns[CheckedPawns.Length] = P;
|
||||
|
||||
if ( damageScale <= 0)
|
||||
{
|
||||
P = none;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Victims = P;
|
||||
P = none;
|
||||
}
|
||||
}
|
||||
|
||||
Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius)
|
||||
* dir,(damageScale * Momentum * dir),DamageType);
|
||||
|
||||
if (Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
|
||||
{
|
||||
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
ExplosionEffect=Class'KFMod.ZEDMKIISecondaryProjectileExplosion'
|
||||
ShrapnelClass=None
|
||||
ExplodeSounds(0)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
|
||||
ExplodeSounds(1)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
|
||||
ExplodeSounds(2)=Sound'KF_FY_ZEDV2SND.Fire.WEP_ZEDV2_Secondary_Fire_S'
|
||||
Speed=0.000000
|
||||
Damage=50.000000
|
||||
DamageRadius=400.000000
|
||||
MyDamageType=Class'ScrnZedPack.DamTypeEMP'
|
||||
DrawType=DT_None
|
||||
bCollideActors=False
|
||||
bBlockZeroExtentTraces=False
|
||||
bBlockNonZeroExtentTraces=False
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
class NiceZedDamageType extends NiceWeaponDamageType
|
||||
abstract;
|
||||
var material HUDDamageTex;
|
||||
var material HUDUberDamageTex;
|
||||
var float HUDTime;
|
||||
defaultproperties
|
||||
{
HUDDamageTex=Texture'KillingFloorHUD.BluntSplashNormal'
HUDUberDamageTex=Shader'KillingFloorHUD.BluntShaderuber'
HUDTime=0.900000
|
||||
}
|
||||
class NiceZedDamageType extends NiceWeaponDamageType
|
||||
abstract;
|
||||
var material HUDDamageTex;
|
||||
var material HUDUberDamageTex;
|
||||
var float HUDTime;
|
||||
defaultproperties
|
||||
{
|
||||
HUDDamageTex=Texture'KillingFloorHUD.BluntSplashNormal'
|
||||
HUDUberDamageTex=Shader'KillingFloorHUD.BluntShaderuber'
|
||||
HUDTime=0.900000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
class NiceZedMeleeDamageType extends NiceZedDamageType;
|
||||
defaultproperties
|
||||
{
DeathString="%o was eaten by %k."
FemaleSuicide="%o ate herself."
MaleSuicide="%o ate himself."
PawnDamageEmitter=Class'ROEffects.ROBloodPuff'
LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore'
LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
|
||||
}
|
||||
class NiceZedMeleeDamageType extends NiceZedDamageType;
|
||||
defaultproperties
|
||||
{
|
||||
DeathString="%o was eaten by %k."
|
||||
FemaleSuicide="%o ate herself."
|
||||
MaleSuicide="%o ate himself."
|
||||
PawnDamageEmitter=Class'ROEffects.ROBloodPuff'
|
||||
LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore'
|
||||
LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
class NiceZedSlashingDamageType extends NiceZedDamageType;
|
||||
defaultproperties
|
||||
{
HUDDamageTex=FinalBlend'KillingFloorHUD.SlashSplashNormalFB'
HUDUberDamageTex=FinalBlend'KillingFloorHUD.SlashSplashUberFB'
DeathString="%o was eaten by %k."
FemaleSuicide="%o ate herself."
MaleSuicide="%o ate himself."
PawnDamageEmitter=Class'ROEffects.ROBloodPuff'
LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore'
LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
|
||||
}
|
||||
class NiceZedSlashingDamageType extends NiceZedDamageType;
|
||||
defaultproperties
|
||||
{
|
||||
HUDDamageTex=FinalBlend'KillingFloorHUD.SlashSplashNormalFB'
|
||||
HUDUberDamageTex=FinalBlend'KillingFloorHUD.SlashSplashUberFB'
|
||||
DeathString="%o was eaten by %k."
|
||||
FemaleSuicide="%o ate herself."
|
||||
MaleSuicide="%o ate himself."
|
||||
PawnDamageEmitter=Class'ROEffects.ROBloodPuff'
|
||||
LowGoreDamageEmitter=Class'ROEffects.ROBloodPuffNoGore'
|
||||
LowDetailEmitter=Class'ROEffects.ROBloodPuffSmall'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,271 +1,622 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBloat extends NiceZombieBloatBase;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var class<FleshHitEmitter> BileExplosion;
|
||||
var class<FleshHitEmitter> BileExplosionHeadless;
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction()
|
||||
{
|
||||
if( bShotAnim )
|
||||
{
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( A!=none )
|
||||
{
bShotAnim = true;
if( !bDecapitated && bDistanceAttackingDoor )
{
SetAnimAction('ZombieBarf');
}
else
{
SetAnimAction('DoorBash');
GotoState('DoorBashing');
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
|
||||
{
bShotAnim = true;
|
||||
// Randomly do a moving attack so the player can't kite the zed
if( FRand() < 0.8 )
{
SetAnimAction('ZombieBarfMoving');
RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
bMovingPukeAttack=true;
}
else
{
SetAnimAction('ZombieBarf');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
{
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
}
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
|
||||
if( NewAction == 'Claw' )
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ZombieBarfMoving' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim('ZombieBarf',, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if ( bGibbed )
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
return;
}
|
||||
if( bDecapitated )
{
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
}
|
||||
}
|
||||
}
|
||||
|
||||
// Barf Time.
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
|
||||
if ( !SavedFireProperties.bInitialized )
|
||||
{
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = Class'KFBloatVomit';
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 500;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = False;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = True;
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
Spawn(Class'KFBloatVomit',,,FireStart,FireRotation);
|
||||
FireStart-=(0.5*CollisionRadius*Y);
|
||||
FireRotation.Yaw -= 1200;
|
||||
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
|
||||
FireStart+=(CollisionRadius*Y);
|
||||
FireRotation.Yaw += 2400;
|
||||
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
|
||||
simulated function Tick(float deltatime)
|
||||
{
|
||||
local vector BileExplosionLoc;
|
||||
local FleshHitEmitter GibBileExplosion;
|
||||
Super.tick(deltatime);
|
||||
if( Role == ROLE_Authority && bMovingPukeAttack )
|
||||
{
// Keep moving toward the target until the timer runs out (anim finishes)
if( RunAttackTimeout > 0 )
{
RunAttackTimeout -= DeltaTime;
|
||||
if( RunAttackTimeout <= 0 )
{
RunAttackTimeout = 0;
bMovingPukeAttack=false;
}
}
|
||||
// Keep the gorefast moving toward its target when attacking
if( bShotAnim && !bWaitForAnim )
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
}
|
||||
// Hack to force animation updates on the server for the bloat if he is relevant to someone
|
||||
// He has glitches when some of his animations don't play on the server. If we
|
||||
// find some other fix for the glitches take this out - Ramm
|
||||
if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
|
||||
{
if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 )
{
bForceSkelUpdate=true;
}
else
{
bForceSkelUpdate=false;
}
|
||||
}
|
||||
if ( Level.NetMode!=NM_DedicatedServer && /*Gored>0*/Health <= 0 && !bPlayBileSplash &&
HitDamageType != class'DamTypeBleedOut' )
|
||||
{
if ( !class'GameInfo'.static.UseLowGore() )
{
BileExplosionLoc = self.Location;
BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
|
||||
if (bDecapitated)
{
GibBileExplosion = Spawn(BileExplosionHeadless,self,, BileExplosionLoc );
}
else
{
GibBileExplosion = Spawn(BileExplosion,self,, BileExplosionLoc );
}
bPlayBileSplash = true;
}
else
{
BileExplosionLoc = self.Location;
BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
|
||||
GibBileExplosion = Spawn(class 'LowGoreBileExplosion',self,, BileExplosionLoc );
bPlayBileSplash = true;
}
|
||||
}
|
||||
}
|
||||
function BileBomb()
|
||||
{
|
||||
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
|
||||
}
|
||||
function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
// local bool AttachSucess;
|
||||
super.PlayDyingAnimation(DamageType, HitLoc);
|
||||
// Don't blow up with bleed out
|
||||
if( bDecapitated && DamageType == class'DamTypeBleedOut' )
|
||||
{
return;
|
||||
}
|
||||
if ( !class'GameInfo'.static.UseLowGore() )
|
||||
{
HideBone(SpineBone2);
|
||||
}
|
||||
if(Role == ROLE_Authority)
|
||||
{
BileBomb();
|
||||
// if(BloatJet!=none)
|
||||
// {
|
||||
// if(Gored < 5)
|
||||
// AttachSucess=AttachToBone(BloatJet,FireRootBone);
|
||||
// // else
|
||||
// // AttachSucess=AttachToBone(BloatJet,SpineBone1);
|
||||
//
|
||||
// if(!AttachSucess)
|
||||
// {
|
||||
// log("DEAD Bloaty Bile didn't like the Boning :o");
|
||||
// BloatJet.SetBase(self);
|
||||
// }
|
||||
// BloatJet.SetRelativeRotation(rot(0,-4096,0));
|
||||
// }
|
||||
}
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
|
||||
if ( class'GameInfo'.static.UseLowGore() )
{
HitFX[SimHitFxTicker].bSever = false;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
return;
}
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
|
||||
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
// Don't do this right now until we get the effects sorted - Ramm
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
local int BoneScaleSlot;
|
||||
local coords boneCoords;
|
||||
local bool bValidBoneToHide;
|
||||
if( boneName == LeftThighBone )
|
||||
{
boneScaleSlot = 0;
bValidBoneToHide = true;
if( SeveredLeftLeg == none )
{
SeveredLeftLeg = Spawn(SeveredLegAttachClass,self);
SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale);
boneCoords = GetBoneCoords( 'lleg' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredLeftLeg, 'lleg');
}
|
||||
}
|
||||
else if ( boneName == RightThighBone )
|
||||
{
boneScaleSlot = 1;
bValidBoneToHide = true;
if( SeveredRightLeg == none )
{
SeveredRightLeg = Spawn(SeveredLegAttachClass,self);
SeveredRightLeg.SetDrawScale(SeveredLegAttachScale);
boneCoords = GetBoneCoords( 'rleg' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredRightLeg, 'rleg');
}
|
||||
}
|
||||
else if( boneName == RightFArmBone )
|
||||
{
boneScaleSlot = 2;
bValidBoneToHide = true;
if( SeveredRightArm == none )
{
SeveredRightArm = Spawn(SeveredArmAttachClass,self);
SeveredRightArm.SetDrawScale(SeveredArmAttachScale);
boneCoords = GetBoneCoords( 'rarm' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredRightArm, 'rarm');
}
|
||||
}
|
||||
else if ( boneName == LeftFArmBone )
|
||||
{
boneScaleSlot = 3;
bValidBoneToHide = true;
if( SeveredLeftArm == none )
{
SeveredLeftArm = Spawn(SeveredArmAttachClass,self);
SeveredLeftArm.SetDrawScale(SeveredArmAttachScale);
boneCoords = GetBoneCoords( 'larm' );
AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredLeftArm, 'larm');
}
|
||||
}
|
||||
else if ( boneName == HeadBone )
|
||||
{
// Only scale the bone down once
if( SeveredHead == none )
{
bValidBoneToHide = true;
boneScaleSlot = 4;
SeveredHead = Spawn(SeveredHeadAttachClass,self);
SeveredHead.SetDrawScale(SeveredHeadAttachScale);
boneCoords = GetBoneCoords( 'neck' );
AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
AttachToBone(SeveredHead, 'neck');
}
else
{
return;
}
|
||||
}
|
||||
else if ( boneName == 'spine' )
|
||||
{
bValidBoneToHide = true;
boneScaleSlot = 5;
|
||||
}
|
||||
else if ( boneName == SpineBone2 )
|
||||
{
bValidBoneToHide = true;
boneScaleSlot = 6;
|
||||
}
|
||||
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
|
||||
if( bValidBoneToHide )
|
||||
{
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
|
||||
}
|
||||
}
|
||||
|
||||
State Dying
|
||||
{
|
||||
function tick(float deltaTime)
|
||||
{
|
||||
if (BloatJet != none)
|
||||
{
|
||||
BloatJet.SetLocation(location);
|
||||
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
|
||||
}
|
||||
super.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
if(damageType == class 'DamTypeVomit' || damageType == class 'DamTypeBlowerThrower')
Damage = 0;
|
||||
else
Super.ModDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
Super.PreCacheStaticMeshes(myLevel);
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.limbs.bloat_head');
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.bloat_diffuse');
|
||||
}
|
||||
defaultproperties
|
||||
{
BileExplosion=Class'KFMod.BileExplosion'
BileExplosionHeadless=Class'KFMod.BileExplosionHeadless'
stunLoopStart=0.100000
stunLoopEnd=0.600000
idleInsertFrame=0.950000
EventClasses(0)="NicePack.NiceZombieBloat"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump'
DetachedArmClass=Class'KFChar.SeveredArmBloat'
DetachedLegClass=Class'KFChar.SeveredLegBloat'
DetachedHeadClass=Class'KFChar.SeveredHeadBloat'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.bloat_cmb'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBloat extends NiceZombieBloatBase;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var class<FleshHitEmitter> BileExplosion;
|
||||
var class<FleshHitEmitter> BileExplosionHeadless;
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction()
|
||||
{
|
||||
if( bShotAnim )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( A!=none )
|
||||
{
|
||||
bShotAnim = true;
|
||||
if( !bDecapitated && bDistanceAttackingDoor )
|
||||
{
|
||||
SetAnimAction('ZombieBarf');
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAnimAction('DoorBash');
|
||||
GotoState('DoorBashing');
|
||||
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
|
||||
SetAnimAction('Claw');
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
SetAnimAction('Claw');
|
||||
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
|
||||
{
|
||||
bShotAnim = true;
|
||||
|
||||
// Randomly do a moving attack so the player can't kite the zed
|
||||
if( FRand() < 0.8 )
|
||||
{
|
||||
SetAnimAction('ZombieBarfMoving');
|
||||
RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
|
||||
bMovingPukeAttack=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAnimAction('ZombieBarf');
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
|
||||
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
|
||||
{
|
||||
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
|
||||
if( NewAction == 'Claw' )
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ZombieBarfMoving' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim('ZombieBarf',, 0.1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
if ( bGibbed )
|
||||
{
|
||||
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
|
||||
return;
|
||||
}
|
||||
|
||||
if( bDecapitated )
|
||||
{
|
||||
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Barf Time.
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
|
||||
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
|
||||
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
|
||||
if ( !SavedFireProperties.bInitialized )
|
||||
{
|
||||
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
|
||||
SavedFireProperties.ProjectileClass = Class'KFBloatVomit';
|
||||
SavedFireProperties.WarnTargetPct = 1;
|
||||
SavedFireProperties.MaxRange = 500;
|
||||
SavedFireProperties.bTossed = False;
|
||||
SavedFireProperties.bTrySplash = False;
|
||||
SavedFireProperties.bLeadTarget = True;
|
||||
SavedFireProperties.bInstantHit = True;
|
||||
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
Spawn(Class'KFBloatVomit',,,FireStart,FireRotation);
|
||||
FireStart-=(0.5*CollisionRadius*Y);
|
||||
FireRotation.Yaw -= 1200;
|
||||
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
|
||||
FireStart+=(CollisionRadius*Y);
|
||||
FireRotation.Yaw += 2400;
|
||||
spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
|
||||
simulated function Tick(float deltatime)
|
||||
{
|
||||
local vector BileExplosionLoc;
|
||||
local FleshHitEmitter GibBileExplosion;
|
||||
Super.tick(deltatime);
|
||||
if( Role == ROLE_Authority && bMovingPukeAttack )
|
||||
{
|
||||
// Keep moving toward the target until the timer runs out (anim finishes)
|
||||
if( RunAttackTimeout > 0 )
|
||||
{
|
||||
RunAttackTimeout -= DeltaTime;
|
||||
|
||||
if( RunAttackTimeout <= 0 )
|
||||
{
|
||||
RunAttackTimeout = 0;
|
||||
bMovingPukeAttack=false;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the gorefast moving toward its target when attacking
|
||||
if( bShotAnim && !bWaitForAnim )
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hack to force animation updates on the server for the bloat if he is relevant to someone
|
||||
// He has glitches when some of his animations don't play on the server. If we
|
||||
// find some other fix for the glitches take this out - Ramm
|
||||
if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
|
||||
{
|
||||
if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0 )
|
||||
{
|
||||
bForceSkelUpdate=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bForceSkelUpdate=false;
|
||||
}
|
||||
}
|
||||
if ( Level.NetMode!=NM_DedicatedServer && /*Gored>0*/Health <= 0 && !bPlayBileSplash &&
|
||||
HitDamageType != class'DamTypeBleedOut' )
|
||||
{
|
||||
if ( !class'GameInfo'.static.UseLowGore() )
|
||||
{
|
||||
BileExplosionLoc = self.Location;
|
||||
BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
|
||||
|
||||
if (bDecapitated)
|
||||
{
|
||||
GibBileExplosion = Spawn(BileExplosionHeadless,self,, BileExplosionLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
GibBileExplosion = Spawn(BileExplosion,self,, BileExplosionLoc );
|
||||
}
|
||||
bPlayBileSplash = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
BileExplosionLoc = self.Location;
|
||||
BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));
|
||||
|
||||
GibBileExplosion = Spawn(class 'LowGoreBileExplosion',self,, BileExplosionLoc );
|
||||
bPlayBileSplash = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
function BileBomb()
|
||||
{
|
||||
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
|
||||
}
|
||||
function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
// local bool AttachSucess;
|
||||
super.PlayDyingAnimation(DamageType, HitLoc);
|
||||
// Don't blow up with bleed out
|
||||
if( bDecapitated && DamageType == class'DamTypeBleedOut' )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( !class'GameInfo'.static.UseLowGore() )
|
||||
{
|
||||
HideBone(SpineBone2);
|
||||
}
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
BileBomb();
|
||||
// if(BloatJet!=none)
|
||||
// {
|
||||
// if(Gored < 5)
|
||||
// AttachSucess=AttachToBone(BloatJet,FireRootBone);
|
||||
// // else
|
||||
// // AttachSucess=AttachToBone(BloatJet,SpineBone1);
|
||||
//
|
||||
// if(!AttachSucess)
|
||||
// {
|
||||
// log("DEAD Bloaty Bile didn't like the Boning :o");
|
||||
// BloatJet.SetBase(self);
|
||||
// }
|
||||
// BloatJet.SetRelativeRotation(rot(0,-4096,0));
|
||||
// }
|
||||
}
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
|
||||
j++;
|
||||
if ( j > 30 )
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
|
||||
continue;
|
||||
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
|
||||
{
|
||||
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
|
||||
bGibbed = true;
|
||||
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
|
||||
if( Level.NetMode == NM_ListenServer )
|
||||
{
|
||||
bDestroyNextTick = true;
|
||||
TimeSetDestroyNextTickTime = Level.TimeSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
|
||||
{
|
||||
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
|
||||
{
|
||||
for( i = 0; i < ArrayCount(HitEffects); i++ )
|
||||
{
|
||||
if( HitEffects[i] == none )
|
||||
continue;
|
||||
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( class'GameInfo'.static.UseLowGore() )
|
||||
{
|
||||
HitFX[SimHitFxTicker].bSever = false;
|
||||
|
||||
switch( HitFX[SimHitFxTicker].bone )
|
||||
{
|
||||
case 'head':
|
||||
if( !bHeadGibbed )
|
||||
{
|
||||
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
|
||||
}
|
||||
|
||||
bHeadGibbed=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( HitFX[SimHitFxTicker].bSever )
|
||||
{
|
||||
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
|
||||
switch( HitFX[SimHitFxTicker].bone )
|
||||
{
|
||||
case 'obliterate':
|
||||
break;
|
||||
|
||||
case LeftThighBone:
|
||||
if( !bLeftLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bLeftLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightThighBone:
|
||||
if( !bRightLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LeftFArmBone:
|
||||
if( !bLeftArmGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
|
||||
bLeftArmGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightFArmBone:
|
||||
if( !bRightArmGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightArmGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'head':
|
||||
if( !bHeadGibbed )
|
||||
{
|
||||
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
|
||||
}
|
||||
|
||||
bHeadGibbed=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't do this right now until we get the effects sorted - Ramm
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
|
||||
HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
|
||||
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
|
||||
HideBone(HitFX[SimHitFxTicker].bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
local int BoneScaleSlot;
|
||||
local coords boneCoords;
|
||||
local bool bValidBoneToHide;
|
||||
|
||||
if( boneName == LeftThighBone )
|
||||
{
|
||||
boneScaleSlot = 0;
|
||||
bValidBoneToHide = true;
|
||||
if( SeveredLeftLeg == none )
|
||||
{
|
||||
SeveredLeftLeg = Spawn(SeveredLegAttachClass,self);
|
||||
SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale);
|
||||
boneCoords = GetBoneCoords( 'lleg' );
|
||||
AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) );
|
||||
AttachToBone(SeveredLeftLeg, 'lleg');
|
||||
}
|
||||
}
|
||||
else if ( boneName == RightThighBone )
|
||||
{
|
||||
boneScaleSlot = 1;
|
||||
bValidBoneToHide = true;
|
||||
if( SeveredRightLeg == none )
|
||||
{
|
||||
SeveredRightLeg = Spawn(SeveredLegAttachClass,self);
|
||||
SeveredRightLeg.SetDrawScale(SeveredLegAttachScale);
|
||||
boneCoords = GetBoneCoords( 'rleg' );
|
||||
AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) );
|
||||
AttachToBone(SeveredRightLeg, 'rleg');
|
||||
}
|
||||
}
|
||||
else if( boneName == RightFArmBone )
|
||||
{
|
||||
boneScaleSlot = 2;
|
||||
bValidBoneToHide = true;
|
||||
if( SeveredRightArm == none )
|
||||
{
|
||||
SeveredRightArm = Spawn(SeveredArmAttachClass,self);
|
||||
SeveredRightArm.SetDrawScale(SeveredArmAttachScale);
|
||||
boneCoords = GetBoneCoords( 'rarm' );
|
||||
AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) );
|
||||
AttachToBone(SeveredRightArm, 'rarm');
|
||||
}
|
||||
}
|
||||
else if ( boneName == LeftFArmBone )
|
||||
{
|
||||
boneScaleSlot = 3;
|
||||
bValidBoneToHide = true;
|
||||
if( SeveredLeftArm == none )
|
||||
{
|
||||
SeveredLeftArm = Spawn(SeveredArmAttachClass,self);
|
||||
SeveredLeftArm.SetDrawScale(SeveredArmAttachScale);
|
||||
boneCoords = GetBoneCoords( 'larm' );
|
||||
AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) );
|
||||
AttachToBone(SeveredLeftArm, 'larm');
|
||||
}
|
||||
}
|
||||
else if ( boneName == HeadBone )
|
||||
{
|
||||
// Only scale the bone down once
|
||||
if( SeveredHead == none )
|
||||
{
|
||||
bValidBoneToHide = true;
|
||||
boneScaleSlot = 4;
|
||||
SeveredHead = Spawn(SeveredHeadAttachClass,self);
|
||||
SeveredHead.SetDrawScale(SeveredHeadAttachScale);
|
||||
boneCoords = GetBoneCoords( 'neck' );
|
||||
AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
|
||||
AttachToBone(SeveredHead, 'neck');
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( boneName == 'spine' )
|
||||
{
|
||||
bValidBoneToHide = true;
|
||||
boneScaleSlot = 5;
|
||||
}
|
||||
else if ( boneName == SpineBone2 )
|
||||
{
|
||||
bValidBoneToHide = true;
|
||||
boneScaleSlot = 6;
|
||||
}
|
||||
// Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
|
||||
if( bValidBoneToHide )
|
||||
{
|
||||
SetBoneScale(BoneScaleSlot, 0.0, BoneName);
|
||||
}
|
||||
}
|
||||
|
||||
State Dying
|
||||
{
|
||||
function tick(float deltaTime)
|
||||
{
|
||||
if (BloatJet != none)
|
||||
{
|
||||
BloatJet.SetLocation(location);
|
||||
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
|
||||
}
|
||||
super.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
if(damageType == class 'DamTypeVomit' || damageType == class 'DamTypeBlowerThrower')
|
||||
Damage = 0;
|
||||
else
|
||||
Super.ModDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
Super.PreCacheStaticMeshes(myLevel);
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.limbs.bloat_head');
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.bloat_diffuse');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
BileExplosion=Class'KFMod.BileExplosion'
|
||||
BileExplosionHeadless=Class'KFMod.BileExplosionHeadless'
|
||||
stunLoopStart=0.100000
|
||||
stunLoopEnd=0.600000
|
||||
idleInsertFrame=0.950000
|
||||
EventClasses(0)="NicePack.NiceZombieBloat"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmBloat'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegBloat'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadBloat'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
|
||||
AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak'
|
||||
Skins(0)=Combiner'KF_Specimens_Trip_T.bloat_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,70 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBloatBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
var BileJet BloatJet;
|
||||
var bool bPlayBileSplash;
|
||||
var bool bMovingPukeAttack;
|
||||
var float RunAttackTimeout;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
StunThreshold=4.000000
fuelRatio=0.250000
clientHeadshotScale=1.500000
MeleeAnims(0)="BloatChop2"
MeleeAnims(1)="BloatChop2"
MeleeAnims(2)="BloatChop2"
BleedOutDuration=6.000000
ZapThreshold=0.500000
ZappedDamageMod=1.500000
bHarpoonToBodyStuns=False
ZombieFlag=1
MeleeDamage=14
damageForce=70000
bFatAss=True
KFRagdollName="Bloat_Trip"
PuntAnim="BloatPunt"
Intelligence=BRAINS_Stupid
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=60.000000)
ColRadius=27.000000
ColHeight=22.000000
SeveredArmAttachScale=1.100000
SeveredLegAttachScale=1.300000
SeveredHeadAttachScale=1.700000
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=5.000000,Z=70.000000)
OnlineHeadshotScale=1.500000
AmmunitionClass=Class'KFMod.BZombieAmmo'
ScoringValue=17
IdleHeavyAnim="BloatIdle"
IdleRifleAnim="BloatIdle"
MeleeRange=30.000000
GroundSpeed=75.000000
WaterSpeed=102.000000
HealthMax=525.000000
Health=525
HeadHeight=2.500000
HeadScale=1.500000
AmbientSoundScaling=8.000000
MenuName="Nice Bloat"
MovementAnims(0)="WalkBloat"
MovementAnims(1)="WalkBloat"
WalkAnims(0)="WalkBloat"
WalkAnims(1)="WalkBloat"
WalkAnims(2)="WalkBloat"
WalkAnims(3)="WalkBloat"
IdleCrouchAnim="BloatIdle"
IdleWeaponAnim="BloatIdle"
IdleRestAnim="BloatIdle"
DrawScale=1.075000
PrePivot=(Z=5.000000)
SoundVolume=200
Mass=400.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieBloatBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
var BileJet BloatJet;
|
||||
var bool bPlayBileSplash;
|
||||
var bool bMovingPukeAttack;
|
||||
var float RunAttackTimeout;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
StunThreshold=4.000000
|
||||
fuelRatio=0.250000
|
||||
clientHeadshotScale=1.500000
|
||||
MeleeAnims(0)="BloatChop2"
|
||||
MeleeAnims(1)="BloatChop2"
|
||||
MeleeAnims(2)="BloatChop2"
|
||||
BleedOutDuration=6.000000
|
||||
ZapThreshold=0.500000
|
||||
ZappedDamageMod=1.500000
|
||||
bHarpoonToBodyStuns=False
|
||||
ZombieFlag=1
|
||||
MeleeDamage=14
|
||||
damageForce=70000
|
||||
bFatAss=True
|
||||
KFRagdollName="Bloat_Trip"
|
||||
PuntAnim="BloatPunt"
|
||||
Intelligence=BRAINS_Stupid
|
||||
bCanDistanceAttackDoors=True
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=60.000000)
|
||||
ColRadius=27.000000
|
||||
ColHeight=22.000000
|
||||
SeveredArmAttachScale=1.100000
|
||||
SeveredLegAttachScale=1.300000
|
||||
SeveredHeadAttachScale=1.700000
|
||||
PlayerCountHealthScale=0.250000
|
||||
OnlineHeadshotOffset=(X=5.000000,Z=70.000000)
|
||||
OnlineHeadshotScale=1.500000
|
||||
AmmunitionClass=Class'KFMod.BZombieAmmo'
|
||||
ScoringValue=17
|
||||
IdleHeavyAnim="BloatIdle"
|
||||
IdleRifleAnim="BloatIdle"
|
||||
MeleeRange=30.000000
|
||||
GroundSpeed=75.000000
|
||||
WaterSpeed=102.000000
|
||||
HealthMax=525.000000
|
||||
Health=525
|
||||
HeadHeight=2.500000
|
||||
HeadScale=1.500000
|
||||
AmbientSoundScaling=8.000000
|
||||
MenuName="Nice Bloat"
|
||||
MovementAnims(0)="WalkBloat"
|
||||
MovementAnims(1)="WalkBloat"
|
||||
WalkAnims(0)="WalkBloat"
|
||||
WalkAnims(1)="WalkBloat"
|
||||
WalkAnims(2)="WalkBloat"
|
||||
WalkAnims(3)="WalkBloat"
|
||||
IdleCrouchAnim="BloatIdle"
|
||||
IdleWeaponAnim="BloatIdle"
|
||||
IdleRestAnim="BloatIdle"
|
||||
DrawScale=1.075000
|
||||
PrePivot=(Z=5.000000)
|
||||
SoundVolume=200
|
||||
Mass=400.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,45 +1,129 @@
|
|||
class NiceZombieBossBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KFPatch2.utx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
var bool bChargingPlayer,bClientCharg,bFireAtWill,bMinigunning,bIsBossView;
|
||||
var float RageStartTime,LastChainGunTime,LastMissileTime,LastSneakedTime;
|
||||
var bool bClientMiniGunning;
|
||||
var name ChargingAnim; // How he runs when charging the player.
|
||||
var byte SyringeCount,ClientSyrCount;
|
||||
var int MGFireCounter;
|
||||
var vector TraceHitPos;
|
||||
var Emitter mTracer,mMuzzleFlash;
|
||||
var bool bClientCloaked;
|
||||
var float LastCheckTimes;
|
||||
var int HealingLevels[3],HealingAmount;
|
||||
var(Sounds) sound RocketFireSound; // The sound of the rocket being fired
|
||||
var(Sounds) sound MiniGunFireSound; // The sound of the minigun being fired
|
||||
var(Sounds) sound MiniGunSpinSound; // The sound of the minigun spinning
|
||||
var(Sounds) sound MeleeImpaleHitSound;// The sound of melee impale attack hitting the player
|
||||
var float MGFireDuration; // How long to fire for this burst
|
||||
var float MGLostSightTimeout; // When to stop firing because we lost sight of the target
|
||||
var() float MGDamage; // How much damage the MG will do
|
||||
var() float ClawMeleeDamageRange;// How long his arms melee strike is
|
||||
var() float ImpaleMeleeDamageRange;// How long his spike melee strike is
|
||||
var float LastChargeTime; // Last time the patriarch charged
|
||||
var float LastForceChargeTime;// Last time patriarch was forced to charge
|
||||
var int NumChargeAttacks; // Number of attacks this charge
|
||||
var float ChargeDamage; // How much damage he's taken since the last charge
|
||||
var float LastDamageTime; // Last Time we took damage
|
||||
// Sneaking
|
||||
var float SneakStartTime; // When did we start sneaking
|
||||
var int SneakCount; // Keep track of the loop that sends the boss to initial hunting state
|
||||
// PipeBomb damage
|
||||
var() float PipeBombDamageScale;// Scale the pipe bomb damage over time
|
||||
replication
|
||||
{
|
||||
reliable if( Role==ROLE_Authority )
bChargingPlayer,SyringeCount,TraceHitPos,bMinigunning,bIsBossView;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
ChargingAnim="RunF"
HealingLevels(0)=5600
HealingLevels(1)=3500
HealingLevels(2)=2187
HealingAmount=1750
MGDamage=6.000000
ClawMeleeDamageRange=85.000000
ImpaleMeleeDamageRange=45.000000
fuelRatio=0.400000
bFrugalFuelUsage=False
clientHeadshotScale=1.200000
ZapThreshold=5.000000
ZappedDamageMod=1.250000
ZapResistanceScale=1.000000
bHarpoonToHeadStuns=False
bHarpoonToBodyStuns=False
ZombieFlag=3
MeleeDamage=75
damageForce=170000
bFatAss=True
KFRagdollName="Patriarch_Trip"
bMeleeStunImmune=True
CrispUpThreshhold=1
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=65.000000)
ColRadius=27.000000
ColHeight=25.000000
SeveredArmAttachScale=1.100000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
PlayerCountHealthScale=0.750000
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkF"
BurningWalkAnims(1)="WalkF"
BurningWalkAnims(2)="WalkF"
OnlineHeadshotOffset=(X=28.000000,Z=75.000000)
OnlineHeadshotScale=1.200000
HeadHealth=100000.000000
MotionDetectorThreat=10.000000
bOnlyDamagedByCrossbow=True
bBoss=True
ScoringValue=500
IdleHeavyAnim="BossIdle"
IdleRifleAnim="BossIdle"
RagDeathVel=80.000000
RagDeathUpKick=100.000000
MeleeRange=10.000000
GroundSpeed=120.000000
WaterSpeed=120.000000
HealthMax=8000.000000
Health=8000
HeadScale=1.300000
MenuName="Nice Patriarch"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkF"
MovementAnims(2)="WalkF"
MovementAnims(3)="WalkF"
AirAnims(0)="JumpInAir"
AirAnims(1)="JumpInAir"
AirAnims(2)="JumpInAir"
AirAnims(3)="JumpInAir"
TakeoffAnims(0)="JumpTakeOff"
TakeoffAnims(1)="JumpTakeOff"
TakeoffAnims(2)="JumpTakeOff"
TakeoffAnims(3)="JumpTakeOff"
LandAnims(0)="JumpLanded"
LandAnims(1)="JumpLanded"
LandAnims(2)="JumpLanded"
LandAnims(3)="JumpLanded"
AirStillAnim="JumpInAir"
TakeoffStillAnim="JumpTakeOff"
IdleCrouchAnim="BossIdle"
IdleWeaponAnim="BossIdle"
IdleRestAnim="BossIdle"
DrawScale=1.050000
PrePivot=(Z=3.000000)
SoundVolume=75
bNetNotify=False
Mass=1000.000000
RotationRate=(Yaw=36000,Roll=0)
|
||||
}
|
||||
class NiceZombieBossBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KFPatch2.utx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
var bool bChargingPlayer,bClientCharg,bFireAtWill,bMinigunning,bIsBossView;
|
||||
var float RageStartTime,LastChainGunTime,LastMissileTime,LastSneakedTime;
|
||||
var bool bClientMiniGunning;
|
||||
var name ChargingAnim; // How he runs when charging the player.
|
||||
var byte SyringeCount,ClientSyrCount;
|
||||
var int MGFireCounter;
|
||||
var vector TraceHitPos;
|
||||
var Emitter mTracer,mMuzzleFlash;
|
||||
var bool bClientCloaked;
|
||||
var float LastCheckTimes;
|
||||
var int HealingLevels[3],HealingAmount;
|
||||
var(Sounds) sound RocketFireSound; // The sound of the rocket being fired
|
||||
var(Sounds) sound MiniGunFireSound; // The sound of the minigun being fired
|
||||
var(Sounds) sound MiniGunSpinSound; // The sound of the minigun spinning
|
||||
var(Sounds) sound MeleeImpaleHitSound;// The sound of melee impale attack hitting the player
|
||||
var float MGFireDuration; // How long to fire for this burst
|
||||
var float MGLostSightTimeout; // When to stop firing because we lost sight of the target
|
||||
var() float MGDamage; // How much damage the MG will do
|
||||
var() float ClawMeleeDamageRange;// How long his arms melee strike is
|
||||
var() float ImpaleMeleeDamageRange;// How long his spike melee strike is
|
||||
var float LastChargeTime; // Last time the patriarch charged
|
||||
var float LastForceChargeTime;// Last time patriarch was forced to charge
|
||||
var int NumChargeAttacks; // Number of attacks this charge
|
||||
var float ChargeDamage; // How much damage he's taken since the last charge
|
||||
var float LastDamageTime; // Last Time we took damage
|
||||
// Sneaking
|
||||
var float SneakStartTime; // When did we start sneaking
|
||||
var int SneakCount; // Keep track of the loop that sends the boss to initial hunting state
|
||||
// PipeBomb damage
|
||||
var() float PipeBombDamageScale;// Scale the pipe bomb damage over time
|
||||
replication
|
||||
{
|
||||
reliable if( Role==ROLE_Authority )
|
||||
bChargingPlayer,SyringeCount,TraceHitPos,bMinigunning,bIsBossView;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
ChargingAnim="RunF"
|
||||
HealingLevels(0)=5600
|
||||
HealingLevels(1)=3500
|
||||
HealingLevels(2)=2187
|
||||
HealingAmount=1750
|
||||
MGDamage=6.000000
|
||||
ClawMeleeDamageRange=85.000000
|
||||
ImpaleMeleeDamageRange=45.000000
|
||||
fuelRatio=0.400000
|
||||
bFrugalFuelUsage=False
|
||||
clientHeadshotScale=1.200000
|
||||
ZapThreshold=5.000000
|
||||
ZappedDamageMod=1.250000
|
||||
ZapResistanceScale=1.000000
|
||||
bHarpoonToHeadStuns=False
|
||||
bHarpoonToBodyStuns=False
|
||||
ZombieFlag=3
|
||||
MeleeDamage=75
|
||||
damageForce=170000
|
||||
bFatAss=True
|
||||
KFRagdollName="Patriarch_Trip"
|
||||
bMeleeStunImmune=True
|
||||
CrispUpThreshhold=1
|
||||
bCanDistanceAttackDoors=True
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=65.000000)
|
||||
ColRadius=27.000000
|
||||
ColHeight=25.000000
|
||||
SeveredArmAttachScale=1.100000
|
||||
SeveredLegAttachScale=1.200000
|
||||
SeveredHeadAttachScale=1.500000
|
||||
PlayerCountHealthScale=0.750000
|
||||
BurningWalkFAnims(0)="WalkF"
|
||||
BurningWalkFAnims(1)="WalkF"
|
||||
BurningWalkFAnims(2)="WalkF"
|
||||
BurningWalkAnims(0)="WalkF"
|
||||
BurningWalkAnims(1)="WalkF"
|
||||
BurningWalkAnims(2)="WalkF"
|
||||
OnlineHeadshotOffset=(X=28.000000,Z=75.000000)
|
||||
OnlineHeadshotScale=1.200000
|
||||
HeadHealth=100000.000000
|
||||
MotionDetectorThreat=10.000000
|
||||
bOnlyDamagedByCrossbow=True
|
||||
bBoss=True
|
||||
ScoringValue=500
|
||||
IdleHeavyAnim="BossIdle"
|
||||
IdleRifleAnim="BossIdle"
|
||||
RagDeathVel=80.000000
|
||||
RagDeathUpKick=100.000000
|
||||
MeleeRange=10.000000
|
||||
GroundSpeed=120.000000
|
||||
WaterSpeed=120.000000
|
||||
HealthMax=8000.000000
|
||||
Health=8000
|
||||
HeadScale=1.300000
|
||||
MenuName="Nice Patriarch"
|
||||
MovementAnims(0)="WalkF"
|
||||
MovementAnims(1)="WalkF"
|
||||
MovementAnims(2)="WalkF"
|
||||
MovementAnims(3)="WalkF"
|
||||
AirAnims(0)="JumpInAir"
|
||||
AirAnims(1)="JumpInAir"
|
||||
AirAnims(2)="JumpInAir"
|
||||
AirAnims(3)="JumpInAir"
|
||||
TakeoffAnims(0)="JumpTakeOff"
|
||||
TakeoffAnims(1)="JumpTakeOff"
|
||||
TakeoffAnims(2)="JumpTakeOff"
|
||||
TakeoffAnims(3)="JumpTakeOff"
|
||||
LandAnims(0)="JumpLanded"
|
||||
LandAnims(1)="JumpLanded"
|
||||
LandAnims(2)="JumpLanded"
|
||||
LandAnims(3)="JumpLanded"
|
||||
AirStillAnim="JumpInAir"
|
||||
TakeoffStillAnim="JumpTakeOff"
|
||||
IdleCrouchAnim="BossIdle"
|
||||
IdleWeaponAnim="BossIdle"
|
||||
IdleRestAnim="BossIdle"
|
||||
DrawScale=1.050000
|
||||
PrePivot=(Z=3.000000)
|
||||
SoundVolume=75
|
||||
bNetNotify=False
|
||||
Mass=1000.000000
|
||||
RotationRate=(Yaw=36000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,190 +1,353 @@
|
|||
//-----------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------
|
||||
class NiceZombieBossController extends KFMonsterController;
|
||||
var NavigationPoint HidingSpots;
|
||||
var float WaitAnimTimeout; // How long until the Anim we are waiting for is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var int AnimWaitChannel; // The channel we are waiting to end in WaitForAnim
|
||||
var name AnimWaitingFor; // The animation we are waiting to end in WaitForAnim, mostly used for debugging
|
||||
var bool bAlreadyFoundEnemy; // The Boss has already found an enemy at least once
|
||||
function bool CanKillMeYet()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else
SetTimer(0.01, True);
|
||||
}
|
||||
// Overridden to support a quick initial attack to get the boss to the players quickly
|
||||
function FightEnemy(bool bCanCharge)
|
||||
{
|
||||
if( KFM.bShotAnim )
|
||||
{
GoToState('WaitForAnim');
Return;
|
||||
}
|
||||
if (KFM.MeleeRange != KFM.default.MeleeRange)
KFM.MeleeRange = KFM.default.MeleeRange;
|
||||
if ( Enemy == none || Enemy.Health <= 0 )
FindNewEnemy();
|
||||
if ( (Enemy == FailedHuntEnemy) && (Level.TimeSeconds == FailedHuntTime) )
|
||||
{
|
||||
// if ( Enemy.Controller.bIsPlayer )
// FindNewEnemy();
|
||||
if ( Enemy == FailedHuntEnemy )
{
GoalString = "FAILED HUNT - HANG OUT";
if ( EnemyVisible() )
bCanCharge = false;
}
|
||||
}
|
||||
if ( !EnemyVisible() )
|
||||
{
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
if( bAlreadyFoundEnemy || NiceZombieBoss(Pawn).SneakCount > 2 )
{
bAlreadyFoundEnemy = true;
GoalString = "Hunt";
GotoState('ZombieHunt');
}
else
{
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
NiceZombieBoss(Pawn).SneakCount++;
GoalString = "InitialHunt";
GotoState('InitialHunting');
}
return;
|
||||
}
|
||||
// see enemy - decide whether to charge it or strafe around/stand and fire
|
||||
Target = Enemy;
|
||||
GoalString = "Charge";
|
||||
PathFindState = 2;
|
||||
DoCharge();
|
||||
}
|
||||
|
||||
// Get the boss to the players quickly after initial spawn
|
||||
state InitialHunting extends Hunting
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
super.SeePlayer(SeenPlayer);
bAlreadyFoundEnemy = true;
GoalString = "Hunt";
GotoState('ZombieHunt');
|
||||
}
|
||||
function BeginState()
|
||||
{
local float ZDif;
|
||||
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
NiceZombieBoss(Pawn).SneakCount++;
|
||||
if( Pawn.CollisionRadius>27 || Pawn.CollisionHeight>46 )
{
ZDif = Pawn.CollisionHeight-44;
Pawn.SetCollisionSize(24,44);
Pawn.MoveSmooth(vect(0,0,-1)*ZDif);
}
|
||||
super.BeginState();
|
||||
}
|
||||
function EndState()
|
||||
{
local float ZDif;
|
||||
if( Pawn.CollisionRadius!=Pawn.Default.CollisionRadius || Pawn.CollisionHeight!=Pawn.Default.CollisionHeight )
{
ZDif = Pawn.Default.CollisionRadius-44;
Pawn.MoveSmooth(vect(0,0,1)*ZDif);
Pawn.SetCollisionSize(Pawn.Default.CollisionRadius,Pawn.Default.CollisionHeight);
}
|
||||
super.EndState();
|
||||
}
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
// I suspect this function causes bloats to get confused
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
if ( Monster(Pawn).bShotAnim )
|
||||
{
Goto('Moving');
|
||||
}
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state RunSomewhere
|
||||
{
|
||||
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
|
||||
function BeginState()
|
||||
{
HidingSpots = none;
Enemy = none;
SetTimer(0.1,True);
|
||||
}
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
SetEnemy(SeenPlayer);
|
||||
}
|
||||
function Timer()
|
||||
{
if( Enemy==none )
Return;
Target = Enemy;
KFM.RangedAttack(Target);
|
||||
}
|
||||
Begin:
|
||||
if( Pawn.Physics==PHYS_Falling )
WaitForLanding();
|
||||
While( KFM.bShotAnim )
Sleep(0.25);
|
||||
if( HidingSpots==none )
HidingSpots = FindRandomDest();
|
||||
if( HidingSpots==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( ActorReachable(HidingSpots) )
|
||||
{
MoveTarget = HidingSpots;
HidingSpots = none;
|
||||
}
|
||||
else FindBestPathToward(HidingSpots,True,False);
|
||||
if( MoveTarget==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 )
MoveToward(MoveTarget,Enemy,,False);
|
||||
else MoveToward(MoveTarget,MoveTarget,,False);
|
||||
if( HidingSpots==none || !PlayerSeesMe() )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
GoTo'Begin';
|
||||
}
|
||||
State SyrRetreat
|
||||
{
|
||||
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
|
||||
function BeginState()
|
||||
{
HidingSpots = none;
Enemy = none;
SetTimer(0.1,True);
|
||||
}
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
SetEnemy(SeenPlayer);
|
||||
}
|
||||
function Timer()
|
||||
{
if( Enemy==none )
Return;
Target = Enemy;
KFM.RangedAttack(Target);
|
||||
}
|
||||
function FindHideSpot()
|
||||
{
local NavigationPoint N,BN;
local float Dist,BDist,MDist;
local vector EnemyDir;
|
||||
if( Enemy==none )
{
HidingSpots = FindRandomDest();
Return;
}
EnemyDir = Normal(Enemy.Location-Pawn.Location);
For( N=Level.NavigationPointList; N!=none; N=N.NextNavigationPoint )
{
MDist = VSize(N.Location-Pawn.Location);
if( MDist<2500 && !FastTrace(N.Location,Enemy.Location) && FindPathToward(N)!=none )
{
Dist = VSize(N.Location-Enemy.Location)/FMax(MDist/800.f,1.5);
if( (EnemyDir Dot Normal(Enemy.Location-N.Location))<0.2 )
Dist/=10;
if( BN==none || BDist<Dist )
{
BN = N;
BDist = Dist;
}
}
}
if( BN==none )
HidingSpots = FindRandomDest();
else HidingSpots = BN;
|
||||
}
|
||||
Begin:
|
||||
if( Pawn.Physics==PHYS_Falling )
WaitForLanding();
|
||||
While( KFM.bShotAnim )
Sleep(0.25);
|
||||
if( HidingSpots==none )
FindHideSpot();
|
||||
if( HidingSpots==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( ActorReachable(HidingSpots) )
|
||||
{
MoveTarget = HidingSpots;
HidingSpots = none;
|
||||
}
|
||||
else FindBestPathToward(HidingSpots,True,False);
|
||||
if( MoveTarget==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 )
MoveToward(MoveTarget,Enemy,,False);
|
||||
else MoveToward(MoveTarget,MoveTarget,,False);
|
||||
if( HidingSpots==none )
NiceZombieBoss(Pawn).BeginHealing();
|
||||
GoTo'Begin';
|
||||
}
|
||||
function bool PlayerSeesMe()
|
||||
{
|
||||
local Controller C;
|
||||
For( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
if( C.bIsPlayer && C.Pawn!=none && C.Pawn!=Pawn && LineOfSightTo(C.Pawn) )
Return True;
|
||||
}
|
||||
Return False;
|
||||
}
|
||||
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
|
||||
// for the Patriach getting stuck in its idle anim on a dedicated server when it
|
||||
// is supposed to doing something. For some reason, on a dedicated server only, it
|
||||
// never gets an animend call for some of the anims, instead the anim gets
|
||||
// interrupted by the idle anim. If we figure that bug out, we can
|
||||
// probably take this out in the future. But for now the fix works - Ramm
|
||||
function SetWaitForAnimTimout(float NewWaitAnimTimeout, name AnimToWaitFor)
|
||||
{
|
||||
WaitAnimTimeout = NewWaitAnimTimeout;
|
||||
AnimWaitingFor = AnimToWaitFor;
|
||||
}
|
||||
state WaitForAnim
|
||||
{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
|
||||
// The anim has ended, clear the flags and let the AI do its thing
|
||||
function WaitTimeout()
|
||||
{
if( bUseFreezeHack )
{
if( Pawn!=none )
{
Pawn.AccelRate = Pawn.Default.AccelRate;
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
}
bUseFreezeHack = False;
}
|
||||
AnimEnd(AnimWaitChannel);
|
||||
}
|
||||
event AnimEnd(int Channel)
|
||||
{
/*local name Sequence;
local float Frame, Rate;
|
||||
Pawn.GetAnimParams( KFMonster(Pawn).ExpectingChannel, Sequence, Frame, Rate );
|
||||
log(GetStateName()$" AnimEnd for Exp Chan "$KFMonster(Pawn).ExpectingChannel$" = "$Sequence$" Channel = "$Channel);
|
||||
Pawn.GetAnimParams( 0, Sequence, Frame, Rate );
log(GetStateName()$" AnimEnd for Chan 0 = "$Sequence);
|
||||
Pawn.GetAnimParams( 1, Sequence, Frame, Rate );
log(GetStateName()$" AnimEnd for Chan 1 = "$Sequence);
|
||||
log(GetStateName()$" AnimEnd bShotAnim = "$Monster(Pawn).bShotAnim); */
|
||||
Pawn.AnimEnd(Channel);
if ( !Monster(Pawn).bShotAnim )
WhatToDoNext(99);
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
Global.Tick(Delta);
|
||||
if( WaitAnimTimeout > 0 )
{
WaitAnimTimeout -= Delta;
|
||||
if( WaitAnimTimeout <= 0 )
{
WaitAnimTimeout = 0;
WaitTimeout();
}
}
|
||||
if( bUseFreezeHack )
{
MoveTarget = none;
MoveTimer = -1;
Pawn.Acceleration = vect(0,0,0);
Pawn.GroundSpeed = 1;
Pawn.AccelRate = 0;
}
|
||||
}
|
||||
function EndState()
|
||||
{
super.EndState();
|
||||
AnimWaitingFor = '';
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
//-----------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------
|
||||
class NiceZombieBossController extends KFMonsterController;
|
||||
var NavigationPoint HidingSpots;
|
||||
var float WaitAnimTimeout; // How long until the Anim we are waiting for is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var int AnimWaitChannel; // The channel we are waiting to end in WaitForAnim
|
||||
var name AnimWaitingFor; // The animation we are waiting to end in WaitForAnim, mostly used for debugging
|
||||
var bool bAlreadyFoundEnemy; // The Boss has already found an enemy at least once
|
||||
function bool CanKillMeYet()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
|
||||
SetCombatTimer();
|
||||
else
|
||||
SetTimer(0.01, True);
|
||||
}
|
||||
// Overridden to support a quick initial attack to get the boss to the players quickly
|
||||
function FightEnemy(bool bCanCharge)
|
||||
{
|
||||
if( KFM.bShotAnim )
|
||||
{
|
||||
GoToState('WaitForAnim');
|
||||
Return;
|
||||
}
|
||||
if (KFM.MeleeRange != KFM.default.MeleeRange)
|
||||
KFM.MeleeRange = KFM.default.MeleeRange;
|
||||
if ( Enemy == none || Enemy.Health <= 0 )
|
||||
FindNewEnemy();
|
||||
if ( (Enemy == FailedHuntEnemy) && (Level.TimeSeconds == FailedHuntTime) )
|
||||
{
|
||||
// if ( Enemy.Controller.bIsPlayer )
|
||||
// FindNewEnemy();
|
||||
|
||||
if ( Enemy == FailedHuntEnemy )
|
||||
{
|
||||
GoalString = "FAILED HUNT - HANG OUT";
|
||||
if ( EnemyVisible() )
|
||||
bCanCharge = false;
|
||||
}
|
||||
}
|
||||
if ( !EnemyVisible() )
|
||||
{
|
||||
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
|
||||
if( bAlreadyFoundEnemy || NiceZombieBoss(Pawn).SneakCount > 2 )
|
||||
{
|
||||
bAlreadyFoundEnemy = true;
|
||||
GoalString = "Hunt";
|
||||
GotoState('ZombieHunt');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
|
||||
NiceZombieBoss(Pawn).SneakCount++;
|
||||
GoalString = "InitialHunt";
|
||||
GotoState('InitialHunting');
|
||||
}
|
||||
return;
|
||||
}
|
||||
// see enemy - decide whether to charge it or strafe around/stand and fire
|
||||
Target = Enemy;
|
||||
GoalString = "Charge";
|
||||
PathFindState = 2;
|
||||
DoCharge();
|
||||
}
|
||||
|
||||
// Get the boss to the players quickly after initial spawn
|
||||
state InitialHunting extends Hunting
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
super.SeePlayer(SeenPlayer);
|
||||
bAlreadyFoundEnemy = true;
|
||||
GoalString = "Hunt";
|
||||
GotoState('ZombieHunt');
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
local float ZDif;
|
||||
|
||||
// Added sneakcount hack to try and fix the endless loop crash. Try and track down what was causing this later - Ramm
|
||||
NiceZombieBoss(Pawn).SneakCount++;
|
||||
|
||||
if( Pawn.CollisionRadius>27 || Pawn.CollisionHeight>46 )
|
||||
{
|
||||
ZDif = Pawn.CollisionHeight-44;
|
||||
Pawn.SetCollisionSize(24,44);
|
||||
Pawn.MoveSmooth(vect(0,0,-1)*ZDif);
|
||||
}
|
||||
|
||||
super.BeginState();
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
local float ZDif;
|
||||
|
||||
if( Pawn.CollisionRadius!=Pawn.Default.CollisionRadius || Pawn.CollisionHeight!=Pawn.Default.CollisionHeight )
|
||||
{
|
||||
ZDif = Pawn.Default.CollisionRadius-44;
|
||||
Pawn.MoveSmooth(vect(0,0,1)*ZDif);
|
||||
Pawn.SetCollisionSize(Pawn.Default.CollisionRadius,Pawn.Default.CollisionHeight);
|
||||
}
|
||||
|
||||
super.EndState();
|
||||
}
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// I suspect this function causes bloats to get confused
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
|
||||
Disable('NotifyBump');
|
||||
Target = Enemy;
|
||||
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
if ( Monster(Pawn).bShotAnim )
|
||||
{
|
||||
Goto('Moving');
|
||||
}
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
|
||||
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
|
||||
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state RunSomewhere
|
||||
{
|
||||
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
|
||||
function BeginState()
|
||||
{
|
||||
HidingSpots = none;
|
||||
Enemy = none;
|
||||
SetTimer(0.1,True);
|
||||
}
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
SetEnemy(SeenPlayer);
|
||||
}
|
||||
function Timer()
|
||||
{
|
||||
if( Enemy==none )
|
||||
Return;
|
||||
Target = Enemy;
|
||||
KFM.RangedAttack(Target);
|
||||
}
|
||||
Begin:
|
||||
if( Pawn.Physics==PHYS_Falling )
|
||||
WaitForLanding();
|
||||
While( KFM.bShotAnim )
|
||||
Sleep(0.25);
|
||||
if( HidingSpots==none )
|
||||
HidingSpots = FindRandomDest();
|
||||
if( HidingSpots==none )
|
||||
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( ActorReachable(HidingSpots) )
|
||||
{
|
||||
MoveTarget = HidingSpots;
|
||||
HidingSpots = none;
|
||||
}
|
||||
else FindBestPathToward(HidingSpots,True,False);
|
||||
if( MoveTarget==none )
|
||||
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 )
|
||||
MoveToward(MoveTarget,Enemy,,False);
|
||||
else MoveToward(MoveTarget,MoveTarget,,False);
|
||||
if( HidingSpots==none || !PlayerSeesMe() )
|
||||
NiceZombieBoss(Pawn).BeginHealing();
|
||||
GoTo'Begin';
|
||||
}
|
||||
State SyrRetreat
|
||||
{
|
||||
Ignores HearNoise,DamageAttitudeTo,Tick,EnemyChanged,Startle;
|
||||
function BeginState()
|
||||
{
|
||||
HidingSpots = none;
|
||||
Enemy = none;
|
||||
SetTimer(0.1,True);
|
||||
}
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
SetEnemy(SeenPlayer);
|
||||
}
|
||||
function Timer()
|
||||
{
|
||||
if( Enemy==none )
|
||||
Return;
|
||||
Target = Enemy;
|
||||
KFM.RangedAttack(Target);
|
||||
}
|
||||
function FindHideSpot()
|
||||
{
|
||||
local NavigationPoint N,BN;
|
||||
local float Dist,BDist,MDist;
|
||||
local vector EnemyDir;
|
||||
|
||||
if( Enemy==none )
|
||||
{
|
||||
HidingSpots = FindRandomDest();
|
||||
Return;
|
||||
}
|
||||
EnemyDir = Normal(Enemy.Location-Pawn.Location);
|
||||
For( N=Level.NavigationPointList; N!=none; N=N.NextNavigationPoint )
|
||||
{
|
||||
MDist = VSize(N.Location-Pawn.Location);
|
||||
if( MDist<2500 && !FastTrace(N.Location,Enemy.Location) && FindPathToward(N)!=none )
|
||||
{
|
||||
Dist = VSize(N.Location-Enemy.Location)/FMax(MDist/800.f,1.5);
|
||||
if( (EnemyDir Dot Normal(Enemy.Location-N.Location))<0.2 )
|
||||
Dist/=10;
|
||||
if( BN==none || BDist<Dist )
|
||||
{
|
||||
BN = N;
|
||||
BDist = Dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( BN==none )
|
||||
HidingSpots = FindRandomDest();
|
||||
else HidingSpots = BN;
|
||||
}
|
||||
Begin:
|
||||
if( Pawn.Physics==PHYS_Falling )
|
||||
WaitForLanding();
|
||||
While( KFM.bShotAnim )
|
||||
Sleep(0.25);
|
||||
if( HidingSpots==none )
|
||||
FindHideSpot();
|
||||
if( HidingSpots==none )
|
||||
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( ActorReachable(HidingSpots) )
|
||||
{
|
||||
MoveTarget = HidingSpots;
|
||||
HidingSpots = none;
|
||||
}
|
||||
else FindBestPathToward(HidingSpots,True,False);
|
||||
if( MoveTarget==none )
|
||||
NiceZombieBoss(Pawn).BeginHealing();
|
||||
if( Enemy!=none && VSize(Enemy.Location-Pawn.Location)<100 )
|
||||
MoveToward(MoveTarget,Enemy,,False);
|
||||
else MoveToward(MoveTarget,MoveTarget,,False);
|
||||
if( HidingSpots==none )
|
||||
NiceZombieBoss(Pawn).BeginHealing();
|
||||
GoTo'Begin';
|
||||
}
|
||||
function bool PlayerSeesMe()
|
||||
{
|
||||
local Controller C;
|
||||
For( C=Level.ControllerList; C!=none; C=C.NextController )
|
||||
{
|
||||
if( C.bIsPlayer && C.Pawn!=none && C.Pawn!=Pawn && LineOfSightTo(C.Pawn) )
|
||||
Return True;
|
||||
}
|
||||
Return False;
|
||||
}
|
||||
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
|
||||
// for the Patriach getting stuck in its idle anim on a dedicated server when it
|
||||
// is supposed to doing something. For some reason, on a dedicated server only, it
|
||||
// never gets an animend call for some of the anims, instead the anim gets
|
||||
// interrupted by the idle anim. If we figure that bug out, we can
|
||||
// probably take this out in the future. But for now the fix works - Ramm
|
||||
function SetWaitForAnimTimout(float NewWaitAnimTimeout, name AnimToWaitFor)
|
||||
{
|
||||
WaitAnimTimeout = NewWaitAnimTimeout;
|
||||
AnimWaitingFor = AnimToWaitFor;
|
||||
}
|
||||
state WaitForAnim
|
||||
{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
|
||||
// The anim has ended, clear the flags and let the AI do its thing
|
||||
function WaitTimeout()
|
||||
{
|
||||
if( bUseFreezeHack )
|
||||
{
|
||||
if( Pawn!=none )
|
||||
{
|
||||
Pawn.AccelRate = Pawn.Default.AccelRate;
|
||||
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
|
||||
}
|
||||
bUseFreezeHack = False;
|
||||
}
|
||||
|
||||
AnimEnd(AnimWaitChannel);
|
||||
}
|
||||
event AnimEnd(int Channel)
|
||||
{
|
||||
/*local name Sequence;
|
||||
local float Frame, Rate;
|
||||
|
||||
Pawn.GetAnimParams( KFMonster(Pawn).ExpectingChannel, Sequence, Frame, Rate );
|
||||
|
||||
log(GetStateName()$" AnimEnd for Exp Chan "$KFMonster(Pawn).ExpectingChannel$" = "$Sequence$" Channel = "$Channel);
|
||||
|
||||
Pawn.GetAnimParams( 0, Sequence, Frame, Rate );
|
||||
log(GetStateName()$" AnimEnd for Chan 0 = "$Sequence);
|
||||
|
||||
Pawn.GetAnimParams( 1, Sequence, Frame, Rate );
|
||||
log(GetStateName()$" AnimEnd for Chan 1 = "$Sequence);
|
||||
|
||||
log(GetStateName()$" AnimEnd bShotAnim = "$Monster(Pawn).bShotAnim); */
|
||||
|
||||
Pawn.AnimEnd(Channel);
|
||||
if ( !Monster(Pawn).bShotAnim )
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
Global.Tick(Delta);
|
||||
|
||||
if( WaitAnimTimeout > 0 )
|
||||
{
|
||||
WaitAnimTimeout -= Delta;
|
||||
|
||||
if( WaitAnimTimeout <= 0 )
|
||||
{
|
||||
WaitAnimTimeout = 0;
|
||||
WaitTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
if( bUseFreezeHack )
|
||||
{
|
||||
MoveTarget = none;
|
||||
MoveTimer = -1;
|
||||
Pawn.Acceleration = vect(0,0,0);
|
||||
Pawn.GroundSpeed = 1;
|
||||
Pawn.AccelRate = 0;
|
||||
}
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
super.EndState();
|
||||
|
||||
AnimWaitingFor = '';
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,255 +1,509 @@
|
|||
class NiceZombieBrute extends NiceZombieBruteBase;
|
||||
var float BlockMeleeDmgMul; //Multiplier for melee damage taken, when Brute is blocking (no matter where the hit was landed)
|
||||
var float HeadShotgunDmgMul; //Multiplier for shotgun damage taken into UNBLOCKED head
|
||||
var float HeadBulletDmgMul; //Multiplier for non-sniper bullet damage taken into UNBLOCKED head
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
super.PostNetBeginPlay();
|
||||
EnableChannelNotify(1,1);
|
||||
EnableChannelNotify(2,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
StartCharging();
|
||||
}
|
||||
function ServerRaiseBlock()
|
||||
{
|
||||
bServerBlock = true;
|
||||
SetAnimAction('BlockLoop');
|
||||
}
|
||||
function ServerLowerBlock()
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
bServerBlock = false;
|
||||
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Sequence == 'BlockLoop')
AnimStopLooping(1);
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
if(bClientCharge != bChargingPlayer)
|
||||
{
bClientCharge = bChargingPlayer;
if (bChargingPlayer)
{
MovementAnims[0] = ChargingAnim;
MeleeAnims[0] = 'BruteRageAttack';
MeleeAnims[1] = 'BruteRageAttack';
MeleeAnims[2] = 'BruteRageAttack';
}
else
{
MovementAnims[0] = default.MovementAnims[0];
MeleeAnims[0] = default.MeleeAnims[0];
MeleeAnims[1] = default.MeleeAnims[1];
MeleeAnims[2] = default.MeleeAnims[2];
}
|
||||
}
|
||||
if (bClientBlock != bServerBlock)
|
||||
{
bClientBlock = bServerBlock;
if (bClientBlock)
SetAnimAction('BlockLoop');
else
{
GetAnimParams(1, Sequence, Frame, Rate);
if (Sequence == 'BlockLoop')
AnimStopLooping(1);
}
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
if (Role == ROLE_Authority)
|
||||
{
// Lock to target when attacking (except on beginner!)
if (bShotAnim && LookTarget != none)
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
// Block according to rules
if (Role == ROLE_Authority && !bServerBlock && !bShotAnim)
if (Controller != none && Controller.Target != none)
ServerRaiseBlock();
|
||||
}
|
||||
}
|
||||
// Override to always move when attacking
|
||||
function RangedAttack(Actor A){
|
||||
if (bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if (CanAttack(A))
|
||||
{
if (bChargingPlayer)
SetAnimAction('AoeClaw');
else
{
if (Rand(BlockHitsLanded) < 1)
SetAnimAction('BlockClaw');
else
SetAnimAction('Claw');
}
bShotAnim = true;
return;
|
||||
}
|
||||
}
|
||||
function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
|
||||
{
|
||||
local float D;
|
||||
local float AddScale;
|
||||
local bool bIsBlocking;
|
||||
bBlockedHS = false;
|
||||
if (bServerBlock && !IsTweening(1))
|
||||
{
bIsBlocking = true;
AddScale = AdditionalScale + BlockAddScale;
|
||||
}
|
||||
else
AddScale = AdditionalScale + 1.0;
|
||||
if (Super.IsHeadShot(Loc, Ray, AddScale))
|
||||
{
if (bIsBlocking)
{
D = vector(Rotation) dot Ray;
if (-D > 0.20) {
bBlockedHS = true;
return false;
}
else
return true;
}
else
return true;
|
||||
}
|
||||
else
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch( int flinchScore,
Pawn instigatedBy,
Vector hitLocation,
Vector momentum,
class<NiceWeaponDamageType> damageType,
float headshotLevel,
KFPlayerReplicationInfo KFPRI){
|
||||
return false;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local float D;
|
||||
local bool bIsHeadshot;
|
||||
local bool bIsBlocking;
|
||||
bBlockedHS = false;
|
||||
if(bServerBlock && !IsTweening(1))
bIsBlocking = true;
|
||||
if(headshotLevel > 0.0 && bIsBlocking){
D = vector(Rotation) dot Normal(Momentum);
D *= -1;
if(D > 0.30) {
bBlockedHS = true;
headshotLevel = 0.0;
}
|
||||
}
|
||||
bIsHeadShot = (headshotLevel > 0.0);
|
||||
// damage, which doen't make headshots, always does full damage to Brute -- PooSH
|
||||
if (damageType != none && damageType.default.bCheckForHeadShots) {
if (!bIsHeadShot && bBlockedHS)
{
if(damageType != none || damageType.default.bIsProjectile)
PlaySound(class'MetalHitEmitter'.default.ImpactSounds[rand(3)],, 128);
else if(class<NiceDamageTypeVetBerserker>(damageType) != none)
PlaySound(Sound'KF_KnifeSnd.Knife_HitMetal',, 128);
if(damageType.default.bDealBurningDamage && !damageType.default.bIsPowerWeapon)
Damage *= BlockFireDmgMul; // Fire damage isn't reduced as much, excluding TrenchGun
else
Damage *= BlockDmgMul; // Greatly reduce damage as we only hit the metal plating
}
else if(bServerBlock && class<NiceDamageTypeVetBerserker>(damageType) != none)
Damage *= BlockMeleeDmgMul; // Give Brute higher melee damage resistance, but apply it only if Brute is blocking
else if(bIsHeadShot){
if (damageType.default.bIsPowerWeapon)
Damage *= HeadShotgunDmgMul; // Give Brute's head resistance to stotguns
}
|
||||
}
|
||||
// Record damage over 2-second frames
|
||||
if (LastDamagedTime < Level.TimeSeconds)
|
||||
{
TwoSecondDamageTotal = 0;
LastDamagedTime = Level.TimeSeconds + 2;
|
||||
}
|
||||
TwoSecondDamageTotal += Damage;
|
||||
// If criteria is met make him rage
|
||||
if (!bDecapitated && !bChargingPlayer && TwoSecondDamageTotal > RageDamageThreshold)
StartCharging();
|
||||
Super(NiceMonster).TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if (bDecapitated)
Died(InstigatedBy.Controller, damageType, HitLocation);
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator)
|
||||
{
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
// Adjust movement speed if not charging
|
||||
if (!bChargingPlayer)
|
||||
{
if (bBurnified)
GroundSpeed = GetOriginalGroundSpeed() * BurnGroundSpeedMul;
else
GroundSpeed = GetOriginalGroundSpeed();
|
||||
}
|
||||
}
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local KFHumanPawn HumanTarget;
|
||||
local float UsedMeleeDamage;
|
||||
local Actor OldTarget;
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
local bool bHitSomeone;
|
||||
if (MeleeDamage > 1)
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
else
UsedMeleeDamage = MeleeDamage;
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Controller != none && Controller.Target != none)
|
||||
{
if (Sequence == 'BruteRageAttack')
{
OldTarget = Controller.Target;
foreach VisibleCollidingActors(class'KFHumanPawn', HumanTarget, MeleeRange + class'KFHumanPawn'.default.CollisionRadius)
{
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, HumanTarget);
}
Controller.Target = OldTarget;
if (bHitSomeone)
BlockHitsLanded++;
}
else if (Sequence != 'BruteAttack1' && Sequence != 'BruteAttack2' && Sequence != 'DoorBash') // Block attack
{
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
if (bHitSomeone)
BlockHitsLanded++;
}
else
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
if (bHitSomeone)
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
|
||||
}
|
||||
}
|
||||
function bool ClawDamageSingleTarget(float UsedMeleeDamage, Actor ThisTarget)
|
||||
{
|
||||
local Pawn HumanTarget;
|
||||
local KFPlayerController HumanTargetController;
|
||||
local bool bHitSomeone;
|
||||
local float EnemyAngle;
|
||||
local vector PushForceVar;
|
||||
EnemyAngle = Normal(ThisTarget.Location - Location) dot vector(Rotation);
|
||||
if (EnemyAngle > 0)
|
||||
{
Controller.Target = ThisTarget;
if (MeleeDamageTarget(UsedMeleeDamage, vect(0, 0, 0)))
{
HumanTarget = KFHumanPawn(ThisTarget);
if (HumanTarget != none)
{
EnemyAngle = (EnemyAngle * 0.5) + 0.5; // Players at sides get knocked back half as much
PushForceVar = (PushForce * Normal(HumanTarget.Location - Location) * EnemyAngle) + PushAdd;
if (!bChargingPlayer)
PushForceVar *= 0.85;
// (!) I'm sure the VeterancyName string is localized but I'm not sure of another way compatible with ServerPerks
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill != none)
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill
.default.VeterancyName == "Berserker")
PushForceVar *= 0.75;
if (!(HumanTarget.Physics == PHYS_WALKING || HumanTarget.Physics == PHYS_none))
PushForceVar *= vect(1, 1, 0); // (!) Don't throw upwards if we are not on the ground - adjust for more flexibility
|
||||
HumanTarget.AddVelocity(PushForceVar);
|
||||
HumanTargetController = KFPlayerController(HumanTarget.Controller);
if (HumanTargetController != none)
HumanTargetController.ShakeView(ShakeViewRotMag, ShakeViewRotRate, ShakeViewRotTime,
ShakeViewOffsetMag, ShakeViewOffsetRate, ShakeViewOffsetTime);
bHitSomeone = true;
}
}
|
||||
}
|
||||
return bHitSomeone;
|
||||
}
|
||||
function StartCharging()
|
||||
{
|
||||
// How many times should we hit before we cool down?
|
||||
if (Level.Game.NumPlayers <= 3)
MaxRageCounter = 2;
|
||||
else
MaxRageCounter = 3;
|
||||
RageCounter = MaxRageCounter;
|
||||
PlaySound(RageSound, SLOT_Talk, 255);
|
||||
GotoState('RageCharging');
|
||||
}
|
||||
state RageCharging
|
||||
{
|
||||
Ignores StartCharging;
|
||||
function bool CanGetOutOfWay()
|
||||
{
return false;
|
||||
}
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
bFrustrated = false;
bChargingPlayer = true;
RageSpeedTween = 0.0;
if (Level.NetMode != NM_DedicatedServer)
ClientChargingAnims();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function EndState()
|
||||
{
bChargingPlayer = false;
|
||||
NiceZombieBruteController(Controller).RageFrustrationTimer = 0;
|
||||
if (Health > 0)
{
GroundSpeed = GetOriginalGroundSpeed();
if (bBurnified)
GroundSpeed *= BurnGroundSpeedMul;
}
|
||||
if( Level.NetMode!=NM_DedicatedServer )
ClientChargingAnims();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function Tick(float Delta)
|
||||
{
if (!bShotAnim)
{
RageSpeedTween = FClamp(RageSpeedTween + (Delta * 0.75), 0, 1.0);
GroundSpeed = OriginalGroundSpeed + ((OriginalGroundSpeed * 0.75 / MaxRageCounter * (RageCounter + 1) * RageSpeedTween));
if (bBurnified)
GroundSpeed *= BurnGroundSpeedMul;
}
|
||||
Global.Tick(Delta);
|
||||
}
|
||||
function bool MeleeDamageTarget(int HitDamage, vector PushDir)
|
||||
{
local bool DamDone, bWasEnemy;
|
||||
bWasEnemy = (Controller.Target == Controller.Enemy);
|
||||
DamDone = Super.MeleeDamageTarget(HitDamage * RageDamageMul, vect(0, 0, 0));
if(Controller == none)
return true;
|
||||
if (bWasEnemy && DamDone)
{
//ChangeTarget();
CalmDown();
}
|
||||
return DamDone;
|
||||
}
|
||||
function CalmDown()
|
||||
{
RageCounter = FClamp(RageCounter - 1, 0, MaxRageCounter);
if (RageCounter == 0)
GotoState('');
|
||||
}
|
||||
function ChangeTarget()
|
||||
{
local Controller C;
local Pawn BestPawn;
local float Dist, BestDist;
for (C = Level.ControllerList; C != none; C = C.NextController)
if (C.Pawn != none && KFHumanPawn(C.Pawn) != none)
{
Dist = VSize(C.Pawn.Location - Location);
if (C.Pawn == Controller.Target)
Dist += GroundSpeed * 4;
if (BestPawn == none)
{
BestPawn = C.Pawn;
BestDist = Dist;
}
else if (Dist < BestDist)
{
BestPawn = C.Pawn;
BestDist = Dist;
}
}
if (BestPawn != none && BestPawn != Controller.Enemy)
MonsterController(Controller).ChangeEnemy(BestPawn, Controller.CanSee(BestPawn));
|
||||
}
|
||||
}
|
||||
// Override to prevent stunning
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Shouldn't fight with our own
|
||||
function bool SameSpeciesAs(Pawn P)
|
||||
{
|
||||
return (NiceZombieBrute(P) != none);
|
||||
}
|
||||
// ------------------------------------------------------
|
||||
// Animation --------------------------------------------
|
||||
// ------------------------------------------------------
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if (NewAction=='')
return;
|
||||
if (NewAction == 'Claw')
|
||||
{
NewAction = MeleeAnims[rand(2)];
|
||||
}
|
||||
else if (NewAction == 'BlockClaw')
|
||||
{
NewAction = 'BruteBlockSlam';
|
||||
}
|
||||
else if (NewAction == 'AoeClaw')
|
||||
{
NewAction = 'BruteRageAttack';
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if (AnimNeedsWait(NewAction))
bWaitForAnim = true;
|
||||
else
bWaitForAnim = false;
|
||||
if (Level.NetMode != NM_Client)
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if (AnimName=='BruteAttack1' || AnimName=='BruteAttack2' || AnimName=='ZombieFireGun' || AnimName == 'DoorBash')
|
||||
{
if (Role == ROLE_Authority)
ServerLowerBlock();
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
else if (AnimName == 'BruteRageAttack')
|
||||
{
if (Role == ROLE_Authority)
ServerLowerBlock();
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
else if (AnimName == 'BlockLoop')
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
LoopAnim(AnimName,, 0.25, 1);
return 1;
|
||||
}
|
||||
else if (AnimName == 'BruteBlockSlam')
|
||||
{
AnimBlendParams(2, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 2);
return 2;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if (TestAnim == 'DoorBash')
return true;
|
||||
return false;
|
||||
}
|
||||
simulated function AnimEnd(int Channel)
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
GetAnimParams(Channel, Sequence, Frame, Rate);
|
||||
// Don't allow notification for a looping animation
|
||||
if (Sequence == 'BlockLoop')
return;
|
||||
// Disable channel 2 when we're done with it
|
||||
if (Channel == 2 && Sequence == 'BruteBlockSlam')
|
||||
{
AnimBlendParams(2, 0);
bShotAnim = false;
return;
|
||||
}
|
||||
Super.AnimEnd(Channel);
|
||||
}
|
||||
simulated function ClientChargingAnims()
|
||||
{
|
||||
PostNetReceive();
|
||||
}
|
||||
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx)
|
||||
{
|
||||
local Actor A;
|
||||
if (bBlockedHS)
A = Spawn(class'NiceBlockHitEmitter', InstigatedBy,, HitLocation, rotator(Normal(HitLocation - Location)));
|
||||
else
Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx);
|
||||
}
|
||||
defaultproperties
|
||||
{
BlockMeleeDmgMul=1.000000
HeadShotgunDmgMul=1.000000
HeadBulletDmgMul=1.000000
stunLoopStart=0.130000
stunLoopEnd=0.650000
idleInsertFrame=0.950000
DetachedArmClass=Class'ScrnZedPack.SeveredArmBrute'
DetachedLegClass=Class'ScrnZedPack.SeveredLegBrute'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadBrute'
ControllerClass=Class'NicePack.NiceZombieBruteController'
|
||||
}
|
||||
class NiceZombieBrute extends NiceZombieBruteBase;
|
||||
var float BlockMeleeDmgMul; //Multiplier for melee damage taken, when Brute is blocking (no matter where the hit was landed)
|
||||
var float HeadShotgunDmgMul; //Multiplier for shotgun damage taken into UNBLOCKED head
|
||||
var float HeadBulletDmgMul; //Multiplier for non-sniper bullet damage taken into UNBLOCKED head
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
super.PostNetBeginPlay();
|
||||
EnableChannelNotify(1,1);
|
||||
EnableChannelNotify(2,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
StartCharging();
|
||||
}
|
||||
function ServerRaiseBlock()
|
||||
{
|
||||
bServerBlock = true;
|
||||
SetAnimAction('BlockLoop');
|
||||
}
|
||||
function ServerLowerBlock()
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
bServerBlock = false;
|
||||
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Sequence == 'BlockLoop')
|
||||
AnimStopLooping(1);
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
if(bClientCharge != bChargingPlayer)
|
||||
{
|
||||
bClientCharge = bChargingPlayer;
|
||||
if (bChargingPlayer)
|
||||
{
|
||||
MovementAnims[0] = ChargingAnim;
|
||||
MeleeAnims[0] = 'BruteRageAttack';
|
||||
MeleeAnims[1] = 'BruteRageAttack';
|
||||
MeleeAnims[2] = 'BruteRageAttack';
|
||||
}
|
||||
else
|
||||
{
|
||||
MovementAnims[0] = default.MovementAnims[0];
|
||||
MeleeAnims[0] = default.MeleeAnims[0];
|
||||
MeleeAnims[1] = default.MeleeAnims[1];
|
||||
MeleeAnims[2] = default.MeleeAnims[2];
|
||||
}
|
||||
}
|
||||
if (bClientBlock != bServerBlock)
|
||||
{
|
||||
bClientBlock = bServerBlock;
|
||||
if (bClientBlock)
|
||||
SetAnimAction('BlockLoop');
|
||||
else
|
||||
{
|
||||
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Sequence == 'BlockLoop')
|
||||
AnimStopLooping(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
// Lock to target when attacking (except on beginner!)
|
||||
if (bShotAnim && LookTarget != none)
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
// Block according to rules
|
||||
if (Role == ROLE_Authority && !bServerBlock && !bShotAnim)
|
||||
if (Controller != none && Controller.Target != none)
|
||||
ServerRaiseBlock();
|
||||
}
|
||||
}
|
||||
// Override to always move when attacking
|
||||
function RangedAttack(Actor A){
|
||||
if (bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if (CanAttack(A))
|
||||
{
|
||||
if (bChargingPlayer)
|
||||
SetAnimAction('AoeClaw');
|
||||
else
|
||||
{
|
||||
if (Rand(BlockHitsLanded) < 1)
|
||||
SetAnimAction('BlockClaw');
|
||||
else
|
||||
SetAnimAction('Claw');
|
||||
}
|
||||
bShotAnim = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
function bool IsHeadShot(vector Loc, vector Ray, float AdditionalScale)
|
||||
{
|
||||
local float D;
|
||||
local float AddScale;
|
||||
local bool bIsBlocking;
|
||||
bBlockedHS = false;
|
||||
if (bServerBlock && !IsTweening(1))
|
||||
{
|
||||
bIsBlocking = true;
|
||||
AddScale = AdditionalScale + BlockAddScale;
|
||||
}
|
||||
else
|
||||
AddScale = AdditionalScale + 1.0;
|
||||
if (Super.IsHeadShot(Loc, Ray, AddScale))
|
||||
{
|
||||
if (bIsBlocking)
|
||||
{
|
||||
D = vector(Rotation) dot Ray;
|
||||
if (-D > 0.20) {
|
||||
bBlockedHS = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch( int flinchScore,
|
||||
Pawn instigatedBy,
|
||||
Vector hitLocation,
|
||||
Vector momentum,
|
||||
class<NiceWeaponDamageType> damageType,
|
||||
float headshotLevel,
|
||||
KFPlayerReplicationInfo KFPRI){
|
||||
return false;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local float D;
|
||||
local bool bIsHeadshot;
|
||||
local bool bIsBlocking;
|
||||
bBlockedHS = false;
|
||||
if(bServerBlock && !IsTweening(1))
|
||||
bIsBlocking = true;
|
||||
if(headshotLevel > 0.0 && bIsBlocking){
|
||||
D = vector(Rotation) dot Normal(Momentum);
|
||||
D *= -1;
|
||||
if(D > 0.30) {
|
||||
bBlockedHS = true;
|
||||
headshotLevel = 0.0;
|
||||
}
|
||||
}
|
||||
bIsHeadShot = (headshotLevel > 0.0);
|
||||
// damage, which doen't make headshots, always does full damage to Brute -- PooSH
|
||||
if (damageType != none && damageType.default.bCheckForHeadShots) {
|
||||
if (!bIsHeadShot && bBlockedHS)
|
||||
{
|
||||
if(damageType != none || damageType.default.bIsProjectile)
|
||||
PlaySound(class'MetalHitEmitter'.default.ImpactSounds[rand(3)],, 128);
|
||||
else if(class<NiceDamageTypeVetBerserker>(damageType) != none)
|
||||
PlaySound(Sound'KF_KnifeSnd.Knife_HitMetal',, 128);
|
||||
if(damageType.default.bDealBurningDamage && !damageType.default.bIsPowerWeapon)
|
||||
Damage *= BlockFireDmgMul; // Fire damage isn't reduced as much, excluding TrenchGun
|
||||
else
|
||||
Damage *= BlockDmgMul; // Greatly reduce damage as we only hit the metal plating
|
||||
}
|
||||
else if(bServerBlock && class<NiceDamageTypeVetBerserker>(damageType) != none)
|
||||
Damage *= BlockMeleeDmgMul; // Give Brute higher melee damage resistance, but apply it only if Brute is blocking
|
||||
else if(bIsHeadShot){
|
||||
if (damageType.default.bIsPowerWeapon)
|
||||
Damage *= HeadShotgunDmgMul; // Give Brute's head resistance to stotguns
|
||||
}
|
||||
}
|
||||
// Record damage over 2-second frames
|
||||
if (LastDamagedTime < Level.TimeSeconds)
|
||||
{
|
||||
TwoSecondDamageTotal = 0;
|
||||
LastDamagedTime = Level.TimeSeconds + 2;
|
||||
}
|
||||
TwoSecondDamageTotal += Damage;
|
||||
// If criteria is met make him rage
|
||||
if (!bDecapitated && !bChargingPlayer && TwoSecondDamageTotal > RageDamageThreshold)
|
||||
StartCharging();
|
||||
Super(NiceMonster).TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if (bDecapitated)
|
||||
Died(InstigatedBy.Controller, damageType, HitLocation);
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator)
|
||||
{
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
// Adjust movement speed if not charging
|
||||
if (!bChargingPlayer)
|
||||
{
|
||||
if (bBurnified)
|
||||
GroundSpeed = GetOriginalGroundSpeed() * BurnGroundSpeedMul;
|
||||
else
|
||||
GroundSpeed = GetOriginalGroundSpeed();
|
||||
}
|
||||
}
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local KFHumanPawn HumanTarget;
|
||||
local float UsedMeleeDamage;
|
||||
local Actor OldTarget;
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
local bool bHitSomeone;
|
||||
if (MeleeDamage > 1)
|
||||
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
else
|
||||
UsedMeleeDamage = MeleeDamage;
|
||||
GetAnimParams(1, Sequence, Frame, Rate);
|
||||
if (Controller != none && Controller.Target != none)
|
||||
{
|
||||
if (Sequence == 'BruteRageAttack')
|
||||
{
|
||||
OldTarget = Controller.Target;
|
||||
foreach VisibleCollidingActors(class'KFHumanPawn', HumanTarget, MeleeRange + class'KFHumanPawn'.default.CollisionRadius)
|
||||
{
|
||||
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, HumanTarget);
|
||||
}
|
||||
Controller.Target = OldTarget;
|
||||
if (bHitSomeone)
|
||||
BlockHitsLanded++;
|
||||
}
|
||||
else if (Sequence != 'BruteAttack1' && Sequence != 'BruteAttack2' && Sequence != 'DoorBash') // Block attack
|
||||
{
|
||||
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
|
||||
if (bHitSomeone)
|
||||
BlockHitsLanded++;
|
||||
}
|
||||
else
|
||||
bHitSomeone = ClawDamageSingleTarget(UsedMeleeDamage, Controller.Target);
|
||||
if (bHitSomeone)
|
||||
PlaySound(MeleeAttackHitSound, SLOT_Interact, 1.25);
|
||||
}
|
||||
}
|
||||
function bool ClawDamageSingleTarget(float UsedMeleeDamage, Actor ThisTarget)
|
||||
{
|
||||
local Pawn HumanTarget;
|
||||
local KFPlayerController HumanTargetController;
|
||||
local bool bHitSomeone;
|
||||
local float EnemyAngle;
|
||||
local vector PushForceVar;
|
||||
EnemyAngle = Normal(ThisTarget.Location - Location) dot vector(Rotation);
|
||||
if (EnemyAngle > 0)
|
||||
{
|
||||
Controller.Target = ThisTarget;
|
||||
if (MeleeDamageTarget(UsedMeleeDamage, vect(0, 0, 0)))
|
||||
{
|
||||
HumanTarget = KFHumanPawn(ThisTarget);
|
||||
if (HumanTarget != none)
|
||||
{
|
||||
EnemyAngle = (EnemyAngle * 0.5) + 0.5; // Players at sides get knocked back half as much
|
||||
PushForceVar = (PushForce * Normal(HumanTarget.Location - Location) * EnemyAngle) + PushAdd;
|
||||
if (!bChargingPlayer)
|
||||
PushForceVar *= 0.85;
|
||||
// (!) I'm sure the VeterancyName string is localized but I'm not sure of another way compatible with ServerPerks
|
||||
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill != none)
|
||||
if (KFPlayerReplicationInfo(HumanTarget.Controller.PlayerReplicationInfo).ClientVeteranSkill
|
||||
.default.VeterancyName == "Berserker")
|
||||
PushForceVar *= 0.75;
|
||||
if (!(HumanTarget.Physics == PHYS_WALKING || HumanTarget.Physics == PHYS_none))
|
||||
PushForceVar *= vect(1, 1, 0); // (!) Don't throw upwards if we are not on the ground - adjust for more flexibility
|
||||
|
||||
HumanTarget.AddVelocity(PushForceVar);
|
||||
|
||||
HumanTargetController = KFPlayerController(HumanTarget.Controller);
|
||||
if (HumanTargetController != none)
|
||||
HumanTargetController.ShakeView(ShakeViewRotMag, ShakeViewRotRate, ShakeViewRotTime,
|
||||
ShakeViewOffsetMag, ShakeViewOffsetRate, ShakeViewOffsetTime);
|
||||
bHitSomeone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bHitSomeone;
|
||||
}
|
||||
function StartCharging()
|
||||
{
|
||||
// How many times should we hit before we cool down?
|
||||
if (Level.Game.NumPlayers <= 3)
|
||||
MaxRageCounter = 2;
|
||||
else
|
||||
MaxRageCounter = 3;
|
||||
RageCounter = MaxRageCounter;
|
||||
PlaySound(RageSound, SLOT_Talk, 255);
|
||||
GotoState('RageCharging');
|
||||
}
|
||||
state RageCharging
|
||||
{
|
||||
Ignores StartCharging;
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool CanSpeedAdjust()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
bFrustrated = false;
|
||||
bChargingPlayer = true;
|
||||
RageSpeedTween = 0.0;
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
ClientChargingAnims();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
bChargingPlayer = false;
|
||||
|
||||
NiceZombieBruteController(Controller).RageFrustrationTimer = 0;
|
||||
|
||||
if (Health > 0)
|
||||
{
|
||||
GroundSpeed = GetOriginalGroundSpeed();
|
||||
if (bBurnified)
|
||||
GroundSpeed *= BurnGroundSpeedMul;
|
||||
}
|
||||
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
ClientChargingAnims();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function Tick(float Delta)
|
||||
{
|
||||
if (!bShotAnim)
|
||||
{
|
||||
RageSpeedTween = FClamp(RageSpeedTween + (Delta * 0.75), 0, 1.0);
|
||||
GroundSpeed = OriginalGroundSpeed + ((OriginalGroundSpeed * 0.75 / MaxRageCounter * (RageCounter + 1) * RageSpeedTween));
|
||||
if (bBurnified)
|
||||
GroundSpeed *= BurnGroundSpeedMul;
|
||||
}
|
||||
|
||||
Global.Tick(Delta);
|
||||
}
|
||||
function bool MeleeDamageTarget(int HitDamage, vector PushDir)
|
||||
{
|
||||
local bool DamDone, bWasEnemy;
|
||||
|
||||
bWasEnemy = (Controller.Target == Controller.Enemy);
|
||||
|
||||
DamDone = Super.MeleeDamageTarget(HitDamage * RageDamageMul, vect(0, 0, 0));
|
||||
if(Controller == none)
|
||||
return true;
|
||||
|
||||
if (bWasEnemy && DamDone)
|
||||
{
|
||||
//ChangeTarget();
|
||||
CalmDown();
|
||||
}
|
||||
|
||||
return DamDone;
|
||||
}
|
||||
function CalmDown()
|
||||
{
|
||||
RageCounter = FClamp(RageCounter - 1, 0, MaxRageCounter);
|
||||
if (RageCounter == 0)
|
||||
GotoState('');
|
||||
}
|
||||
function ChangeTarget()
|
||||
{
|
||||
local Controller C;
|
||||
local Pawn BestPawn;
|
||||
local float Dist, BestDist;
|
||||
for (C = Level.ControllerList; C != none; C = C.NextController)
|
||||
if (C.Pawn != none && KFHumanPawn(C.Pawn) != none)
|
||||
{
|
||||
Dist = VSize(C.Pawn.Location - Location);
|
||||
if (C.Pawn == Controller.Target)
|
||||
Dist += GroundSpeed * 4;
|
||||
if (BestPawn == none)
|
||||
{
|
||||
BestPawn = C.Pawn;
|
||||
BestDist = Dist;
|
||||
}
|
||||
else if (Dist < BestDist)
|
||||
{
|
||||
BestPawn = C.Pawn;
|
||||
BestDist = Dist;
|
||||
}
|
||||
}
|
||||
if (BestPawn != none && BestPawn != Controller.Enemy)
|
||||
MonsterController(Controller).ChangeEnemy(BestPawn, Controller.CanSee(BestPawn));
|
||||
}
|
||||
}
|
||||
// Override to prevent stunning
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Shouldn't fight with our own
|
||||
function bool SameSpeciesAs(Pawn P)
|
||||
{
|
||||
return (NiceZombieBrute(P) != none);
|
||||
}
|
||||
// ------------------------------------------------------
|
||||
// Animation --------------------------------------------
|
||||
// ------------------------------------------------------
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if (NewAction=='')
|
||||
return;
|
||||
if (NewAction == 'Claw')
|
||||
{
|
||||
NewAction = MeleeAnims[rand(2)];
|
||||
}
|
||||
else if (NewAction == 'BlockClaw')
|
||||
{
|
||||
NewAction = 'BruteBlockSlam';
|
||||
}
|
||||
else if (NewAction == 'AoeClaw')
|
||||
{
|
||||
NewAction = 'BruteRageAttack';
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if (AnimNeedsWait(NewAction))
|
||||
bWaitForAnim = true;
|
||||
else
|
||||
bWaitForAnim = false;
|
||||
if (Level.NetMode != NM_Client)
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if (AnimName=='BruteAttack1' || AnimName=='BruteAttack2' || AnimName=='ZombieFireGun' || AnimName == 'DoorBash')
|
||||
{
|
||||
if (Role == ROLE_Authority)
|
||||
ServerLowerBlock();
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
else if (AnimName == 'BruteRageAttack')
|
||||
{
|
||||
if (Role == ROLE_Authority)
|
||||
ServerLowerBlock();
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
else if (AnimName == 'BlockLoop')
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
LoopAnim(AnimName,, 0.25, 1);
|
||||
return 1;
|
||||
}
|
||||
else if (AnimName == 'BruteBlockSlam')
|
||||
{
|
||||
AnimBlendParams(2, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 2);
|
||||
return 2;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if (TestAnim == 'DoorBash')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
simulated function AnimEnd(int Channel)
|
||||
{
|
||||
local name Sequence;
|
||||
local float Frame, Rate;
|
||||
GetAnimParams(Channel, Sequence, Frame, Rate);
|
||||
// Don't allow notification for a looping animation
|
||||
if (Sequence == 'BlockLoop')
|
||||
return;
|
||||
// Disable channel 2 when we're done with it
|
||||
if (Channel == 2 && Sequence == 'BruteBlockSlam')
|
||||
{
|
||||
AnimBlendParams(2, 0);
|
||||
bShotAnim = false;
|
||||
return;
|
||||
}
|
||||
Super.AnimEnd(Channel);
|
||||
}
|
||||
simulated function ClientChargingAnims()
|
||||
{
|
||||
PostNetReceive();
|
||||
}
|
||||
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIdx)
|
||||
{
|
||||
local Actor A;
|
||||
if (bBlockedHS)
|
||||
A = Spawn(class'NiceBlockHitEmitter', InstigatedBy,, HitLocation, rotator(Normal(HitLocation - Location)));
|
||||
else
|
||||
Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitIdx);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
BlockMeleeDmgMul=1.000000
|
||||
HeadShotgunDmgMul=1.000000
|
||||
HeadBulletDmgMul=1.000000
|
||||
stunLoopStart=0.130000
|
||||
stunLoopEnd=0.650000
|
||||
idleInsertFrame=0.950000
|
||||
DetachedArmClass=Class'ScrnZedPack.SeveredArmBrute'
|
||||
DetachedLegClass=Class'ScrnZedPack.SeveredLegBrute'
|
||||
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadBrute'
|
||||
ControllerClass=Class'NicePack.NiceZombieBruteController'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,129 @@
|
|||
class NiceZombieBruteBase extends NiceMonster;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
#exec OBJ LOAD FILE=KFWeaponSound.uax
|
||||
var bool bChargingPlayer;
|
||||
var bool bClientCharge;
|
||||
var bool bFrustrated;
|
||||
var int MaxRageCounter; // Maximum amount of players we can hit before calming down
|
||||
var int RageCounter; // Decreases each time we successfully hit a player
|
||||
var float RageSpeedTween;
|
||||
var int TwoSecondDamageTotal;
|
||||
var float LastDamagedTime;
|
||||
var int RageDamageThreshold;
|
||||
var int BlockHitsLanded; // Hits made while blocking or raging
|
||||
var name ChargingAnim;
|
||||
var Sound RageSound;
|
||||
// View shaking for players
|
||||
var() vector ShakeViewRotMag;
|
||||
var() vector ShakeViewRotRate;
|
||||
var() float ShakeViewRotTime;
|
||||
var() vector ShakeViewOffsetMag;
|
||||
var() vector ShakeViewOffsetRate;
|
||||
var() float ShakeViewOffsetTime;
|
||||
var float PushForce;
|
||||
var vector PushAdd; // Used to add additional height to push
|
||||
var float RageDamageMul; // Multiplier for hit damage when raging
|
||||
var float RageBumpDamage; // Damage done when we hit other specimens while raging
|
||||
var float BlockAddScale; // Additional head scale when blocking
|
||||
var bool bBlockedHS;
|
||||
var bool bBlocking;
|
||||
var bool bServerBlock;
|
||||
var bool bClientBlock;
|
||||
var float BlockDmgMul; // Multiplier for damage taken from blocked shots
|
||||
var float BlockFireDmgMul;
|
||||
var float BurnGroundSpeedMul; // Multiplier for ground speed when burning
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bChargingPlayer, bServerBlock;
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//--------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
RageDamageThreshold=50
ChargingAnim="BruteRun"
RageSound=SoundGroup'ScrnZedPack_S.Brute.BruteRage'
ShakeViewRotMag=(X=500.000000,Y=500.000000,Z=600.000000)
ShakeViewRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
ShakeViewRotTime=6.000000
ShakeViewOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
ShakeViewOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
ShakeViewOffsetTime=3.500000
PushForce=860.000000
PushAdd=(Z=150.000000)
RageDamageMul=1.100000
RageBumpDamage=4.000000
BlockAddScale=2.500000
BlockDmgMul=0.100000
BlockFireDmgMul=1.000000
BurnGroundSpeedMul=0.700000
StunThreshold=4.000000
flameFuel=0.500000
clientHeadshotScale=1.300000
MeleeAnims(0)="BruteAttack1"
MeleeAnims(1)="BruteAttack2"
MeleeAnims(2)="BruteBlockSlam"
MoanVoice=SoundGroup'ScrnZedPack_S.Brute.BruteTalk'
BleedOutDuration=7.000000
ZombieFlag=3
MeleeDamage=20
damageForce=25000
bFatAss=True
KFRagdollName="FleshPound_Trip"
MeleeAttackHitSound=SoundGroup'ScrnZedPack_S.Brute.BruteHitPlayer'
JumpSound=SoundGroup'ScrnZedPack_S.Brute.BruteJump'
SpinDamConst=20.000000
SpinDamRand=20.000000
bMeleeStunImmune=True
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=35.000000
ColHeight=25.000000
SeveredArmAttachScale=1.300000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
OnlineHeadshotScale=1.300000
HeadHealth=180.000000
PlayerNumHeadHealthScale=0.200000
MotionDetectorThreat=5.000000
HitSound(0)=SoundGroup'ScrnZedPack_S.Brute.BrutePain'
DeathSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteDeath'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
ScoringValue=60
IdleHeavyAnim="BruteIdle"
IdleRifleAnim="BruteIdle"
RagDeathUpKick=100.000000
MeleeRange=85.000000
GroundSpeed=140.000000
WaterSpeed=120.000000
HealthMax=1000.000000
Health=1000
HeadHeight=2.500000
HeadScale=1.300000
MenuName="Brute"
MovementAnims(0)="BruteWalkC"
MovementAnims(1)="BruteWalkC"
WalkAnims(0)="BruteWalkC"
WalkAnims(1)="BruteWalkC"
WalkAnims(2)="RunL"
WalkAnims(3)="RunR"
IdleCrouchAnim="BruteIdle"
IdleWeaponAnim="BruteIdle"
IdleRestAnim="BruteIdle"
AmbientSound=SoundGroup'ScrnZedPack_S.Brute.BruteIdle1Shot'
Mesh=SkeletalMesh'ScrnZedPack_A.BruteMesh'
PrePivot=(Z=0.000000)
Skins(0)=Combiner'ScrnZedPack_T.Brute.Brute_Final'
Mass=600.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
class NiceZombieBruteBase extends NiceMonster;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
#exec OBJ LOAD FILE=KFWeaponSound.uax
|
||||
var bool bChargingPlayer;
|
||||
var bool bClientCharge;
|
||||
var bool bFrustrated;
|
||||
var int MaxRageCounter; // Maximum amount of players we can hit before calming down
|
||||
var int RageCounter; // Decreases each time we successfully hit a player
|
||||
var float RageSpeedTween;
|
||||
var int TwoSecondDamageTotal;
|
||||
var float LastDamagedTime;
|
||||
var int RageDamageThreshold;
|
||||
var int BlockHitsLanded; // Hits made while blocking or raging
|
||||
var name ChargingAnim;
|
||||
var Sound RageSound;
|
||||
// View shaking for players
|
||||
var() vector ShakeViewRotMag;
|
||||
var() vector ShakeViewRotRate;
|
||||
var() float ShakeViewRotTime;
|
||||
var() vector ShakeViewOffsetMag;
|
||||
var() vector ShakeViewOffsetRate;
|
||||
var() float ShakeViewOffsetTime;
|
||||
var float PushForce;
|
||||
var vector PushAdd; // Used to add additional height to push
|
||||
var float RageDamageMul; // Multiplier for hit damage when raging
|
||||
var float RageBumpDamage; // Damage done when we hit other specimens while raging
|
||||
var float BlockAddScale; // Additional head scale when blocking
|
||||
var bool bBlockedHS;
|
||||
var bool bBlocking;
|
||||
var bool bServerBlock;
|
||||
var bool bClientBlock;
|
||||
var float BlockDmgMul; // Multiplier for damage taken from blocked shots
|
||||
var float BlockFireDmgMul;
|
||||
var float BurnGroundSpeedMul; // Multiplier for ground speed when burning
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
|
||||
bChargingPlayer, bServerBlock;
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//--------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
RageDamageThreshold=50
|
||||
ChargingAnim="BruteRun"
|
||||
RageSound=SoundGroup'ScrnZedPack_S.Brute.BruteRage'
|
||||
ShakeViewRotMag=(X=500.000000,Y=500.000000,Z=600.000000)
|
||||
ShakeViewRotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
|
||||
ShakeViewRotTime=6.000000
|
||||
ShakeViewOffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
|
||||
ShakeViewOffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
|
||||
ShakeViewOffsetTime=3.500000
|
||||
PushForce=860.000000
|
||||
PushAdd=(Z=150.000000)
|
||||
RageDamageMul=1.100000
|
||||
RageBumpDamage=4.000000
|
||||
BlockAddScale=2.500000
|
||||
BlockDmgMul=0.100000
|
||||
BlockFireDmgMul=1.000000
|
||||
BurnGroundSpeedMul=0.700000
|
||||
StunThreshold=4.000000
|
||||
flameFuel=0.500000
|
||||
clientHeadshotScale=1.300000
|
||||
MeleeAnims(0)="BruteAttack1"
|
||||
MeleeAnims(1)="BruteAttack2"
|
||||
MeleeAnims(2)="BruteBlockSlam"
|
||||
MoanVoice=SoundGroup'ScrnZedPack_S.Brute.BruteTalk'
|
||||
BleedOutDuration=7.000000
|
||||
ZombieFlag=3
|
||||
MeleeDamage=20
|
||||
damageForce=25000
|
||||
bFatAss=True
|
||||
KFRagdollName="FleshPound_Trip"
|
||||
MeleeAttackHitSound=SoundGroup'ScrnZedPack_S.Brute.BruteHitPlayer'
|
||||
JumpSound=SoundGroup'ScrnZedPack_S.Brute.BruteJump'
|
||||
SpinDamConst=20.000000
|
||||
SpinDamRand=20.000000
|
||||
bMeleeStunImmune=True
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=52.000000)
|
||||
ColRadius=35.000000
|
||||
ColHeight=25.000000
|
||||
SeveredArmAttachScale=1.300000
|
||||
SeveredLegAttachScale=1.200000
|
||||
SeveredHeadAttachScale=1.500000
|
||||
PlayerCountHealthScale=0.250000
|
||||
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
|
||||
OnlineHeadshotScale=1.300000
|
||||
HeadHealth=180.000000
|
||||
PlayerNumHeadHealthScale=0.200000
|
||||
MotionDetectorThreat=5.000000
|
||||
HitSound(0)=SoundGroup'ScrnZedPack_S.Brute.BrutePain'
|
||||
DeathSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteDeath'
|
||||
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
|
||||
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
|
||||
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
|
||||
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Brute.BruteChallenge'
|
||||
ScoringValue=60
|
||||
IdleHeavyAnim="BruteIdle"
|
||||
IdleRifleAnim="BruteIdle"
|
||||
RagDeathUpKick=100.000000
|
||||
MeleeRange=85.000000
|
||||
GroundSpeed=140.000000
|
||||
WaterSpeed=120.000000
|
||||
HealthMax=1000.000000
|
||||
Health=1000
|
||||
HeadHeight=2.500000
|
||||
HeadScale=1.300000
|
||||
MenuName="Brute"
|
||||
MovementAnims(0)="BruteWalkC"
|
||||
MovementAnims(1)="BruteWalkC"
|
||||
WalkAnims(0)="BruteWalkC"
|
||||
WalkAnims(1)="BruteWalkC"
|
||||
WalkAnims(2)="RunL"
|
||||
WalkAnims(3)="RunR"
|
||||
IdleCrouchAnim="BruteIdle"
|
||||
IdleWeaponAnim="BruteIdle"
|
||||
IdleRestAnim="BruteIdle"
|
||||
AmbientSound=SoundGroup'ScrnZedPack_S.Brute.BruteIdle1Shot'
|
||||
Mesh=SkeletalMesh'ScrnZedPack_A.BruteMesh'
|
||||
PrePivot=(Z=0.000000)
|
||||
Skins(0)=Combiner'ScrnZedPack_T.Brute.Brute_Final'
|
||||
Mass=600.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,39 @@
|
|||
class NiceZombieBruteController extends NiceMonsterController;
|
||||
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var bool bDoneSpottedCheck;
|
||||
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
|
||||
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else
SetTimer(0.01, True);
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
function BeginState()
|
||||
{
super.BeginState();
|
||||
RageFrustrationThreshhold = default.RageFrustrationThreshhold + (Frand() * 5);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
RageFrustrationThreshhold=10.000000
|
||||
}
|
||||
class NiceZombieBruteController extends NiceMonsterController;
|
||||
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var bool bDoneSpottedCheck;
|
||||
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
|
||||
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
|
||||
SetCombatTimer();
|
||||
else
|
||||
SetTimer(0.01, True);
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
|
||||
Disable('NotifyBump');
|
||||
Target = Enemy;
|
||||
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
super.BeginState();
|
||||
|
||||
RageFrustrationThreshhold = default.RageFrustrationThreshhold + (Frand() * 5);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
RageFrustrationThreshhold=10.000000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,126 +1,205 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieClot extends NiceZombieClotBase;
|
||||
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local vector PushDir;
|
||||
local KFPawn KFP;
|
||||
local float UsedMeleeDamage;
|
||||
|
||||
if( MeleeDamage > 1 )
|
||||
{
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
}
|
||||
else
|
||||
{
UsedMeleeDamage = MeleeDamage;
|
||||
}
|
||||
// If zombie has latched onto us...
|
||||
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
|
||||
{
KFP = KFPawn(Controller.Target);
|
||||
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
|
||||
if( !bDecapitated && KFP != none )
{
if( KFPlayerReplicationInfo(KFP.PlayerReplicationInfo) == none ||
KFP.GetVeteran().static.CanBeGrabbed(KFPlayerReplicationInfo(KFP.PlayerReplicationInfo), self))
{
if( DisabledPawn != none )
{
DisabledPawn.bMovementDisabled = false;
}
|
||||
KFP.DisableMovement(GrappleDuration);
DisabledPawn = KFP;
}
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
bShotAnim = true;
SetAnimAction('Claw');
return;
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'KnockDown' || TestAnim == 'DoorBash' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ClotGrapple' || AnimName=='ClotGrappleTwo' || AnimName=='ClotGrappleThree' )
|
||||
{
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
// Randomly send out a message about Clot grabbing you(10% chance)
if ( FRand() < 0.10 && LookTarget != none && KFPlayerController(LookTarget.Controller) != none &&
VSizeSquared(Location - LookTarget.Location) < 2500 /* (MeleeRange + 20)^2 */ &&
Level.TimeSeconds - KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime > ClotGrabMessageDelay &&
KFPlayerController(LookTarget.Controller).SelectedVeterancy != class'KFVetBerserker' )
{
PlayerController(LookTarget.Controller).Speech('AUTO', 11, "");
KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime = Level.TimeSeconds;
}
|
||||
bGrappling = true;
GrappleEndTime = Level.TimeSeconds + GrappleDuration;
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
if( bShotAnim && Role == ROLE_Authority )
|
||||
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
|
||||
}
|
||||
if( Role == ROLE_Authority && bGrappling )
|
||||
{
if( Level.TimeSeconds > GrappleEndTime )
{
bGrappling = false;
}
|
||||
}
|
||||
// if we move out of melee range, stop doing the grapple animation
|
||||
if( bGrappling && LookTarget != none )
|
||||
{
if( VSize(LookTarget.Location - Location) > MeleeRange + CollisionRadius + LookTarget.CollisionRadius )
{
bGrappling = false;
AnimEnd(1);
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super.RemoveHead();
|
||||
MeleeAnims[0] = 'Claw';
|
||||
MeleeAnims[1] = 'Claw';
|
||||
MeleeAnims[2] = 'Claw2';
|
||||
MeleeDamage *= 2;
|
||||
MeleeRange *= 2;
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
super.Destroyed();
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
Super.PreCacheStaticMeshes(myLevel);
|
||||
/*
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_1');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_2');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_3');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_4');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_5');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_6');
|
||||
*/
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_spec');
|
||||
}
|
||||
defaultproperties
|
||||
{
idleInsertFrame=0.468000
EventClasses(0)="NicePack.NiceZombieClot"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
DetachedArmClass=Class'KFChar.SeveredArmClot'
DetachedLegClass=Class'KFChar.SeveredLegClot'
DetachedHeadClass=Class'KFChar.SeveredHeadClot'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
AmbientSound=Sound'KF_BaseClot.Clot_Idle1Loop'
Mesh=SkeletalMesh'KF_Freaks_Trip.CLOT_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.clot_cmb'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieClot extends NiceZombieClotBase;
|
||||
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
#exec OBJ LOAD FILE=MeanZedSkins.utx
|
||||
function ClawDamageTarget()
|
||||
{
|
||||
local vector PushDir;
|
||||
local KFPawn KFP;
|
||||
local float UsedMeleeDamage;
|
||||
|
||||
if( MeleeDamage > 1 )
|
||||
{
|
||||
UsedMeleeDamage = (MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1));
|
||||
}
|
||||
else
|
||||
{
|
||||
UsedMeleeDamage = MeleeDamage;
|
||||
}
|
||||
// If zombie has latched onto us...
|
||||
if ( MeleeDamageTarget( UsedMeleeDamage, PushDir))
|
||||
{
|
||||
KFP = KFPawn(Controller.Target);
|
||||
|
||||
PlaySound(MeleeAttackHitSound, SLOT_Interact, 2.0);
|
||||
|
||||
if( !bDecapitated && KFP != none )
|
||||
{
|
||||
if( KFPlayerReplicationInfo(KFP.PlayerReplicationInfo) == none ||
|
||||
KFP.GetVeteran().static.CanBeGrabbed(KFPlayerReplicationInfo(KFP.PlayerReplicationInfo), self))
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
|
||||
DisabledPawn.bMovementDisabled = false;
|
||||
}
|
||||
|
||||
KFP.DisableMovement(GrappleDuration);
|
||||
DisabledPawn = KFP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
|
||||
bShotAnim = true;
|
||||
SetAnimAction('Claw');
|
||||
return;
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'KnockDown' || TestAnim == 'DoorBash' )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ClotGrapple' || AnimName=='ClotGrappleTwo' || AnimName=='ClotGrappleThree' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
|
||||
// Randomly send out a message about Clot grabbing you(10% chance)
|
||||
if ( FRand() < 0.10 && LookTarget != none && KFPlayerController(LookTarget.Controller) != none &&
|
||||
VSizeSquared(Location - LookTarget.Location) < 2500 /* (MeleeRange + 20)^2 */ &&
|
||||
Level.TimeSeconds - KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime > ClotGrabMessageDelay &&
|
||||
KFPlayerController(LookTarget.Controller).SelectedVeterancy != class'KFVetBerserker' )
|
||||
{
|
||||
PlayerController(LookTarget.Controller).Speech('AUTO', 11, "");
|
||||
KFPlayerController(LookTarget.Controller).LastClotGrabMessageTime = Level.TimeSeconds;
|
||||
}
|
||||
|
||||
bGrappling = true;
|
||||
GrappleEndTime = Level.TimeSeconds + GrappleDuration;
|
||||
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
if( bShotAnim && Role == ROLE_Authority )
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
if( Role == ROLE_Authority && bGrappling )
|
||||
{
|
||||
if( Level.TimeSeconds > GrappleEndTime )
|
||||
{
|
||||
bGrappling = false;
|
||||
}
|
||||
}
|
||||
// if we move out of melee range, stop doing the grapple animation
|
||||
if( bGrappling && LookTarget != none )
|
||||
{
|
||||
if( VSize(LookTarget.Location - Location) > MeleeRange + CollisionRadius + LookTarget.CollisionRadius )
|
||||
{
|
||||
bGrappling = false;
|
||||
AnimEnd(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super.RemoveHead();
|
||||
MeleeAnims[0] = 'Claw';
|
||||
MeleeAnims[1] = 'Claw';
|
||||
MeleeAnims[2] = 'Claw2';
|
||||
MeleeDamage *= 2;
|
||||
MeleeRange *= 2;
|
||||
if( DisabledPawn != none )
|
||||
{
|
||||
DisabledPawn.bMovementDisabled = false;
|
||||
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
|
||||
DisabledPawn.bMovementDisabled = false;
|
||||
DisabledPawn = none;
|
||||
}
|
||||
super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
super.Destroyed();
|
||||
if( DisabledPawn != none )
|
||||
{
|
||||
DisabledPawn.bMovementDisabled = false;
|
||||
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
Super.PreCacheStaticMeshes(myLevel);
|
||||
/*
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_1');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_2');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_3');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_4');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_5');
|
||||
myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.clot.clothead_piece_6');
|
||||
*/
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.clot_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.clot_spec');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
idleInsertFrame=0.468000
|
||||
EventClasses(0)="NicePack.NiceZombieClot"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmClot'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegClot'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadClot'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Challenge'
|
||||
AmbientSound=Sound'KF_BaseClot.Clot_Idle1Loop'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.CLOT_Freak'
|
||||
Skins(0)=Combiner'KF_Specimens_Trip_T.clot_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,72 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieClotBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
var KFPawn DisabledPawn; // The pawn that has been disabled by this zombie's grapple
|
||||
var bool bGrappling; // This zombie is grappling someone
|
||||
var float GrappleEndTime; // When the current grapple should be over
|
||||
var() float GrappleDuration; // How long a grapple by this zombie should last
|
||||
var float ClotGrabMessageDelay; // Amount of time between a player saying "I've been grabbed" message
|
||||
replication
|
||||
{
|
||||
reliable if(bNetDirty && Role == ROLE_Authority)
bGrappling;
|
||||
}
|
||||
function BreakGrapple()
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
DisabledPawn.bMovementDisabled = false;
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
GrappleDuration=1.500000
ClotGrabMessageDelay=12.000000
fuelRatio=0.900000
clientHeadshotScale=1.500000
MeleeAnims(0)="ClotGrapple"
MeleeAnims(1)="ClotGrappleTwo"
MeleeAnims(2)="ClotGrappleThree"
bCannibal=True
MeleeDamage=6
damageForce=5000
KFRagdollName="Clot_Trip"
CrispUpThreshhold=9
PuntAnim="ClotPunt"
AdditionalWalkAnims(0)="ClotWalk2"
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.800000
SeveredHeadAttachScale=0.800000
OnlineHeadshotOffset=(X=20.000000,Z=37.000000)
OnlineHeadshotScale=1.300000
MotionDetectorThreat=0.340000
ScoringValue=7
MeleeRange=20.000000
GroundSpeed=105.000000
WaterSpeed=105.000000
JumpZ=340.000000
HealthMax=130.000000
Health=130
MenuName="Nice Clot"
MovementAnims(0)="ClotWalk"
WalkAnims(0)="ClotWalk"
WalkAnims(1)="ClotWalk"
WalkAnims(2)="ClotWalk"
WalkAnims(3)="ClotWalk"
DrawScale=1.100000
PrePivot=(Z=5.000000)
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieClotBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=KF_Freaks_Trip.ukx
|
||||
#exec OBJ LOAD FILE=KF_Specimens_Trip_T.utx
|
||||
var KFPawn DisabledPawn; // The pawn that has been disabled by this zombie's grapple
|
||||
var bool bGrappling; // This zombie is grappling someone
|
||||
var float GrappleEndTime; // When the current grapple should be over
|
||||
var() float GrappleDuration; // How long a grapple by this zombie should last
|
||||
var float ClotGrabMessageDelay; // Amount of time between a player saying "I've been grabbed" message
|
||||
replication
|
||||
{
|
||||
reliable if(bNetDirty && Role == ROLE_Authority)
|
||||
bGrappling;
|
||||
}
|
||||
function BreakGrapple()
|
||||
{
|
||||
if( DisabledPawn != none )
|
||||
{
|
||||
DisabledPawn.bMovementDisabled = false;
|
||||
DisabledPawn = none;
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
GrappleDuration=1.500000
|
||||
ClotGrabMessageDelay=12.000000
|
||||
fuelRatio=0.900000
|
||||
clientHeadshotScale=1.500000
|
||||
MeleeAnims(0)="ClotGrapple"
|
||||
MeleeAnims(1)="ClotGrappleTwo"
|
||||
MeleeAnims(2)="ClotGrappleThree"
|
||||
bCannibal=True
|
||||
MeleeDamage=6
|
||||
damageForce=5000
|
||||
KFRagdollName="Clot_Trip"
|
||||
CrispUpThreshhold=9
|
||||
PuntAnim="ClotPunt"
|
||||
AdditionalWalkAnims(0)="ClotWalk2"
|
||||
Intelligence=BRAINS_Mammal
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=48.000000)
|
||||
ColRadius=25.000000
|
||||
ColHeight=5.000000
|
||||
ExtCollAttachBoneName="Collision_Attach"
|
||||
SeveredArmAttachScale=0.800000
|
||||
SeveredLegAttachScale=0.800000
|
||||
SeveredHeadAttachScale=0.800000
|
||||
OnlineHeadshotOffset=(X=20.000000,Z=37.000000)
|
||||
OnlineHeadshotScale=1.300000
|
||||
MotionDetectorThreat=0.340000
|
||||
ScoringValue=7
|
||||
MeleeRange=20.000000
|
||||
GroundSpeed=105.000000
|
||||
WaterSpeed=105.000000
|
||||
JumpZ=340.000000
|
||||
HealthMax=130.000000
|
||||
Health=130
|
||||
MenuName="Nice Clot"
|
||||
MovementAnims(0)="ClotWalk"
|
||||
WalkAnims(0)="ClotWalk"
|
||||
WalkAnims(1)="ClotWalk"
|
||||
WalkAnims(2)="ClotWalk"
|
||||
WalkAnims(3)="ClotWalk"
|
||||
DrawScale=1.100000
|
||||
PrePivot=(Z=5.000000)
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,90 +1,143 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieCrawler extends NiceZombieCrawlerBase;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
function bool DoPounce()
|
||||
{
|
||||
if (bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5))
return false;
|
||||
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
|
||||
Velocity.Z = JumpZ;
|
||||
SetPhysics(PHYS_Falling);
|
||||
ZombieSpringAnim();
|
||||
bPouncing=true;
|
||||
return true;
|
||||
}
|
||||
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
|
||||
{
|
||||
local int OldHeadHealth;
|
||||
OldHeadHealth = HeadHealth;
|
||||
Super(NiceMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
|
||||
// If crawler's head was damaged, but not yet removed -- I say kill the goddamn thing
|
||||
if(HeadHealth < OldHeadHealth && HeadHealth > 0)
RemoveHead();
|
||||
}
|
||||
simulated function ZombieSpringAnim()
|
||||
{
|
||||
SetAnimAction('ZombieSpring');
|
||||
}
|
||||
event Landed(vector HitNormal)
|
||||
{
|
||||
bPouncing=false;
|
||||
super.Landed(HitNormal);
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
// TODO: is there a better way
|
||||
if(bPouncing && KFHumanPawn(Other)!=none )
|
||||
{
KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType');
if (KFHumanPawn(Other).Health <=0)
{
//TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget
KFHumanPawn(Other).SpawnGibs(self.rotation, 1);
}
//After impact, there'll be no momentum for further bumps
bPouncing=false;
|
||||
}
|
||||
}
|
||||
// Blend his attacks so he can hit you in mid air.
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='HitF' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, NeckBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='ZombieSpring' )
|
||||
{
PlayAnim(AnimName,,0.02);
return 0;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(2);
if( Physics == PHYS_Falling )
{
NewAction = MeleeAirAnims[meleeAnimIndex];
}
else
{
NewAction = meleeAnims[meleeAnimIndex];
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.crawler_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
stunLoopStart=0.110000
stunLoopEnd=0.570000
idleInsertFrame=0.900000
EventClasses(0)="NicePack.NiceZombieCrawler"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Jump'
DetachedArmClass=Class'KFChar.SeveredArmCrawler'
DetachedLegClass=Class'KFChar.SeveredLegCrawler'
DetachedHeadClass=Class'KFChar.SeveredHeadCrawler'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
ControllerClass=Class'NicePack.NiceZombieCrawlerController'
AmbientSound=Sound'KF_BaseCrawler.Crawler_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.Crawler_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.crawler_cmb'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieCrawler extends NiceZombieCrawlerBase;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
function bool DoPounce()
|
||||
{
|
||||
if (bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5))
|
||||
return false;
|
||||
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
|
||||
Velocity.Z = JumpZ;
|
||||
SetPhysics(PHYS_Falling);
|
||||
ZombieSpringAnim();
|
||||
bPouncing=true;
|
||||
return true;
|
||||
}
|
||||
function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
|
||||
{
|
||||
local int OldHeadHealth;
|
||||
OldHeadHealth = HeadHealth;
|
||||
Super(NiceMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
|
||||
// If crawler's head was damaged, but not yet removed -- I say kill the goddamn thing
|
||||
if(HeadHealth < OldHeadHealth && HeadHealth > 0)
|
||||
RemoveHead();
|
||||
}
|
||||
simulated function ZombieSpringAnim()
|
||||
{
|
||||
SetAnimAction('ZombieSpring');
|
||||
}
|
||||
event Landed(vector HitNormal)
|
||||
{
|
||||
bPouncing=false;
|
||||
super.Landed(HitNormal);
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
// TODO: is there a better way
|
||||
if(bPouncing && KFHumanPawn(Other)!=none )
|
||||
{
|
||||
KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType');
|
||||
if (KFHumanPawn(Other).Health <=0)
|
||||
{
|
||||
//TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget
|
||||
KFHumanPawn(Other).SpawnGibs(self.rotation, 1);
|
||||
}
|
||||
//After impact, there'll be no momentum for further bumps
|
||||
bPouncing=false;
|
||||
}
|
||||
}
|
||||
// Blend his attacks so he can hit you in mid air.
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.0, 1);
|
||||
return 1;
|
||||
}
|
||||
if( AnimName=='HitF' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, NeckBone);
|
||||
PlayAnim(AnimName,, 0.0, 1);
|
||||
return 1;
|
||||
}
|
||||
if( AnimName=='ZombieSpring' )
|
||||
{
|
||||
PlayAnim(AnimName,,0.02);
|
||||
return 0;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
|
||||
meleeAnimIndex = Rand(2);
|
||||
if( Physics == PHYS_Falling )
|
||||
{
|
||||
NewAction = MeleeAirAnims[meleeAnimIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.crawler_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.crawler_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
stunLoopStart=0.110000
|
||||
stunLoopEnd=0.570000
|
||||
idleInsertFrame=0.900000
|
||||
EventClasses(0)="NicePack.NiceZombieCrawler"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmCrawler'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegCrawler'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadCrawler'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Crawler.Crawler_Acquire'
|
||||
ControllerClass=Class'NicePack.NiceZombieCrawlerController'
|
||||
AmbientSound=Sound'KF_BaseCrawler.Crawler_Idle'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.Crawler_Freak'
|
||||
Skins(0)=Combiner'KF_Specimens_Trip_T.crawler_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,77 +1,176 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieCrawlerBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var() float PounceSpeed;
|
||||
var bool bPouncing;
|
||||
var(Anims) name MeleeAirAnims[3]; // Attack anims for when flying through the air
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
function bool DoPounce()
|
||||
{
|
||||
if ( bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5) )
return false;
|
||||
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
|
||||
Velocity.Z = JumpZ;
|
||||
SetPhysics(PHYS_Falling);
|
||||
ZombieSpringAnim();
|
||||
bPouncing=true;
|
||||
return true;
|
||||
}
|
||||
simulated function ZombieSpringAnim()
|
||||
{
|
||||
SetAnimAction('ZombieSpring');
|
||||
}
|
||||
event Landed(vector HitNormal)
|
||||
{
|
||||
bPouncing=false;
|
||||
super.Landed(HitNormal);
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
// TODO: is there a better way
|
||||
if(bPouncing && KFHumanPawn(Other)!=none )
|
||||
{
KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType');
if (KFHumanPawn(Other).Health <=0)
{
//TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget
KFHumanPawn(Other).SpawnGibs(self.rotation, 1);
}
//After impact, there'll be no momentum for further bumps
bPouncing=false;
|
||||
}
|
||||
}
|
||||
// Blend his attacks so he can hit you in mid air.
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='HitF' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, NeckBone);
PlayAnim(AnimName,, 0.0, 1);
return 1;
|
||||
}
|
||||
if( AnimName=='ZombieSpring' )
|
||||
{
PlayAnim(AnimName,,0.02);
return 0;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(2);
if( Physics == PHYS_Falling )
{
NewAction = MeleeAirAnims[meleeAnimIndex];
}
else
{
NewAction = meleeAnims[meleeAnimIndex];
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
defaultproperties
|
||||
{
PounceSpeed=330.000000
MeleeAirAnims(0)="InAir_Attack1"
MeleeAirAnims(1)="InAir_Attack2"
StunThreshold=2.000000
fuelRatio=0.800000
bWeakHead=True
clientHeadshotScale=1.350000
MeleeAnims(0)="ZombieLeapAttack"
MeleeAnims(1)="ZombieLeapAttack2"
HitAnims(1)="HitF"
HitAnims(2)="HitF"
KFHitFront="HitF"
KFHitBack="HitF"
KFHitLeft="HitF"
KFHitRight="HitF"
bStunImmune=True
bCannibal=True
ZombieFlag=2
MeleeDamage=6
damageForce=5000
KFRagdollName="Crawler_Trip"
CrispUpThreshhold=10
Intelligence=BRAINS_Mammal
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.850000
SeveredHeadAttachScale=1.100000
OnlineHeadshotOffset=(X=28.000000,Z=7.000000)
OnlineHeadshotScale=1.200000
MotionDetectorThreat=0.340000
ScoringValue=10
IdleHeavyAnim="ZombieLeapIdle"
IdleRifleAnim="ZombieLeapIdle"
bCrawler=True
GroundSpeed=140.000000
WaterSpeed=130.000000
JumpZ=350.000000
HealthMax=70.000000
Health=70
HeadHeight=2.500000
HeadScale=1.050000
MenuName="Nice Crawler"
bDoTorsoTwist=False
MovementAnims(0)="ZombieScuttle"
MovementAnims(1)="ZombieScuttleB"
MovementAnims(2)="ZombieScuttleL"
MovementAnims(3)="ZombieScuttleR"
WalkAnims(0)="ZombieScuttle"
WalkAnims(1)="ZombieScuttleB"
WalkAnims(2)="ZombieScuttleL"
WalkAnims(3)="ZombieScuttleR"
AirAnims(0)="ZombieSpring"
AirAnims(1)="ZombieSpring"
AirAnims(2)="ZombieSpring"
AirAnims(3)="ZombieSpring"
TakeoffAnims(0)="ZombieSpring"
TakeoffAnims(1)="ZombieSpring"
TakeoffAnims(2)="ZombieSpring"
TakeoffAnims(3)="ZombieSpring"
AirStillAnim="ZombieSpring"
TakeoffStillAnim="ZombieLeapIdle"
IdleCrouchAnim="ZombieLeapIdle"
IdleWeaponAnim="ZombieLeapIdle"
IdleRestAnim="ZombieLeapIdle"
bOrientOnSlope=True
DrawScale=1.100000
PrePivot=(Z=0.000000)
CollisionHeight=25.000000
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieCrawlerBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var() float PounceSpeed;
|
||||
var bool bPouncing;
|
||||
var(Anims) name MeleeAirAnims[3]; // Attack anims for when flying through the air
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
function bool DoPounce()
|
||||
{
|
||||
if ( bZapped || bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking) || VSize(Location - Controller.Target.Location) > (MeleeRange * 5) )
|
||||
return false;
|
||||
Velocity = Normal(Controller.Target.Location-Location)*PounceSpeed;
|
||||
Velocity.Z = JumpZ;
|
||||
SetPhysics(PHYS_Falling);
|
||||
ZombieSpringAnim();
|
||||
bPouncing=true;
|
||||
return true;
|
||||
}
|
||||
simulated function ZombieSpringAnim()
|
||||
{
|
||||
SetAnimAction('ZombieSpring');
|
||||
}
|
||||
event Landed(vector HitNormal)
|
||||
{
|
||||
bPouncing=false;
|
||||
super.Landed(HitNormal);
|
||||
}
|
||||
event Bump(actor Other)
|
||||
{
|
||||
// TODO: is there a better way
|
||||
if(bPouncing && KFHumanPawn(Other)!=none )
|
||||
{
|
||||
KFHumanPawn(Other).TakeDamage(((MeleeDamage - (MeleeDamage * 0.05)) + (MeleeDamage * (FRand() * 0.1))), self ,self.Location,self.velocity, class 'NicePack.NiceZedMeleeDamageType');
|
||||
if (KFHumanPawn(Other).Health <=0)
|
||||
{
|
||||
//TODO - move this to humanpawn.takedamage? Also see KFMonster.MeleeDamageTarget
|
||||
KFHumanPawn(Other).SpawnGibs(self.rotation, 1);
|
||||
}
|
||||
//After impact, there'll be no momentum for further bumps
|
||||
bPouncing=false;
|
||||
}
|
||||
}
|
||||
// Blend his attacks so he can hit you in mid air.
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='InAir_Attack1' || AnimName=='InAir_Attack2' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.0, 1);
|
||||
return 1;
|
||||
}
|
||||
if( AnimName=='HitF' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, NeckBone);
|
||||
PlayAnim(AnimName,, 0.0, 1);
|
||||
return 1;
|
||||
}
|
||||
if( AnimName=='ZombieSpring' )
|
||||
{
|
||||
PlayAnim(AnimName,,0.02);
|
||||
return 0;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
|
||||
meleeAnimIndex = Rand(2);
|
||||
if( Physics == PHYS_Falling )
|
||||
{
|
||||
NewAction = MeleeAirAnims[meleeAnimIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'ZombieSpring' || TestAnim == 'DoorBash' )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
PounceSpeed=330.000000
|
||||
MeleeAirAnims(0)="InAir_Attack1"
|
||||
MeleeAirAnims(1)="InAir_Attack2"
|
||||
StunThreshold=2.000000
|
||||
fuelRatio=0.800000
|
||||
bWeakHead=True
|
||||
clientHeadshotScale=1.350000
|
||||
MeleeAnims(0)="ZombieLeapAttack"
|
||||
MeleeAnims(1)="ZombieLeapAttack2"
|
||||
HitAnims(1)="HitF"
|
||||
HitAnims(2)="HitF"
|
||||
KFHitFront="HitF"
|
||||
KFHitBack="HitF"
|
||||
KFHitLeft="HitF"
|
||||
KFHitRight="HitF"
|
||||
bStunImmune=True
|
||||
bCannibal=True
|
||||
ZombieFlag=2
|
||||
MeleeDamage=6
|
||||
damageForce=5000
|
||||
KFRagdollName="Crawler_Trip"
|
||||
CrispUpThreshhold=10
|
||||
Intelligence=BRAINS_Mammal
|
||||
SeveredArmAttachScale=0.800000
|
||||
SeveredLegAttachScale=0.850000
|
||||
SeveredHeadAttachScale=1.100000
|
||||
OnlineHeadshotOffset=(X=28.000000,Z=7.000000)
|
||||
OnlineHeadshotScale=1.200000
|
||||
MotionDetectorThreat=0.340000
|
||||
ScoringValue=10
|
||||
IdleHeavyAnim="ZombieLeapIdle"
|
||||
IdleRifleAnim="ZombieLeapIdle"
|
||||
bCrawler=True
|
||||
GroundSpeed=140.000000
|
||||
WaterSpeed=130.000000
|
||||
JumpZ=350.000000
|
||||
HealthMax=70.000000
|
||||
Health=70
|
||||
HeadHeight=2.500000
|
||||
HeadScale=1.050000
|
||||
MenuName="Nice Crawler"
|
||||
bDoTorsoTwist=False
|
||||
MovementAnims(0)="ZombieScuttle"
|
||||
MovementAnims(1)="ZombieScuttleB"
|
||||
MovementAnims(2)="ZombieScuttleL"
|
||||
MovementAnims(3)="ZombieScuttleR"
|
||||
WalkAnims(0)="ZombieScuttle"
|
||||
WalkAnims(1)="ZombieScuttleB"
|
||||
WalkAnims(2)="ZombieScuttleL"
|
||||
WalkAnims(3)="ZombieScuttleR"
|
||||
AirAnims(0)="ZombieSpring"
|
||||
AirAnims(1)="ZombieSpring"
|
||||
AirAnims(2)="ZombieSpring"
|
||||
AirAnims(3)="ZombieSpring"
|
||||
TakeoffAnims(0)="ZombieSpring"
|
||||
TakeoffAnims(1)="ZombieSpring"
|
||||
TakeoffAnims(2)="ZombieSpring"
|
||||
TakeoffAnims(3)="ZombieSpring"
|
||||
AirStillAnim="ZombieSpring"
|
||||
TakeoffStillAnim="ZombieLeapIdle"
|
||||
IdleCrouchAnim="ZombieLeapIdle"
|
||||
IdleWeaponAnim="ZombieLeapIdle"
|
||||
IdleRestAnim="ZombieLeapIdle"
|
||||
bOrientOnSlope=True
|
||||
DrawScale=1.100000
|
||||
PrePivot=(Z=0.000000)
|
||||
CollisionHeight=25.000000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,94 @@
|
|||
class NiceZombieCrawlerController extends NiceMonsterController;
|
||||
var float LastPounceTime;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Crawler saying something
if ( !KFGameType(Level.Game).bDidSpottedCrawlerMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 18, "");
KFGameType(Level.Game).bDidSpottedCrawlerMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function bool IsInPounceDist(actor PTarget)
|
||||
{
|
||||
local vector DistVec;
|
||||
local float time;
|
||||
local float HeightMoved;
|
||||
local float EndHeight;
|
||||
//work out time needed to reach target
|
||||
DistVec = pawn.location - PTarget.location;
|
||||
DistVec.Z=0;
|
||||
time = vsize(DistVec)/NiceZombieCrawler(pawn).PounceSpeed;
|
||||
// vertical change in that time
|
||||
//assumes downward grav only
|
||||
HeightMoved = Pawn.JumpZ*time + 0.5*pawn.PhysicsVolume.Gravity.z*time*time;
|
||||
EndHeight = pawn.Location.z +HeightMoved;
|
||||
//log(Vsize(Pawn.Location - PTarget.Location));
|
||||
|
||||
if((abs(EndHeight - PTarget.Location.Z) < Pawn.CollisionHeight + PTarget.CollisionHeight) &&
|
||||
VSize(pawn.Location - PTarget.Location) < KFMonster(pawn).MeleeRange * 5)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
function bool FireWeaponAt(Actor A)
|
||||
{
|
||||
local vector aFacing,aToB;
|
||||
local float RelativeDir;
|
||||
if ( A == none )
A = Enemy;
|
||||
if ( (A == none) || (Focus != A) )
return false;
|
||||
if(CanAttack(A))
|
||||
{
Target = A;
Monster(Pawn).RangedAttack(Target);
|
||||
}
|
||||
else
|
||||
{
//TODO - base off land time rather than launch time?
if((LastPounceTime + (4.5 - (FRand() * 3.0))) < Level.TimeSeconds )
{
aFacing=Normal(Vector(Pawn.Rotation));
// Get the vector from A to B
aToB=A.Location-Pawn.Location;
|
||||
RelativeDir = aFacing dot aToB;
if ( RelativeDir > 0.85 )
{
//Facing enemy
if(IsInPounceDist(A) )
{
if(NiceZombieCrawler(Pawn).DoPounce()==true )
LastPounceTime = Level.TimeSeconds;
}
}
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function bool NotifyLanded(vector HitNormal)
|
||||
{
|
||||
if( NiceZombieCrawler(pawn).bPouncing )
|
||||
{
// restart pathfinding from landing location
GotoState('hunting');
return false;
|
||||
}
|
||||
else
|
||||
return super.NotifyLanded(HitNormal);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceZombieCrawlerController extends NiceMonsterController;
|
||||
var float LastPounceTime;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
|
||||
{
|
||||
// 25% chance of first player to see this Crawler saying something
|
||||
if ( !KFGameType(Level.Game).bDidSpottedCrawlerMessage && FRand() < 0.25 )
|
||||
{
|
||||
PlayerController(SeenPlayer.Controller).Speech('AUTO', 18, "");
|
||||
KFGameType(Level.Game).bDidSpottedCrawlerMessage = true;
|
||||
}
|
||||
|
||||
bDoneSpottedCheck = true;
|
||||
}
|
||||
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function bool IsInPounceDist(actor PTarget)
|
||||
{
|
||||
local vector DistVec;
|
||||
local float time;
|
||||
local float HeightMoved;
|
||||
local float EndHeight;
|
||||
//work out time needed to reach target
|
||||
DistVec = pawn.location - PTarget.location;
|
||||
DistVec.Z=0;
|
||||
time = vsize(DistVec)/NiceZombieCrawler(pawn).PounceSpeed;
|
||||
// vertical change in that time
|
||||
//assumes downward grav only
|
||||
HeightMoved = Pawn.JumpZ*time + 0.5*pawn.PhysicsVolume.Gravity.z*time*time;
|
||||
EndHeight = pawn.Location.z +HeightMoved;
|
||||
//log(Vsize(Pawn.Location - PTarget.Location));
|
||||
|
||||
if((abs(EndHeight - PTarget.Location.Z) < Pawn.CollisionHeight + PTarget.CollisionHeight) &&
|
||||
VSize(pawn.Location - PTarget.Location) < KFMonster(pawn).MeleeRange * 5)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
function bool FireWeaponAt(Actor A)
|
||||
{
|
||||
local vector aFacing,aToB;
|
||||
local float RelativeDir;
|
||||
if ( A == none )
|
||||
A = Enemy;
|
||||
if ( (A == none) || (Focus != A) )
|
||||
return false;
|
||||
if(CanAttack(A))
|
||||
{
|
||||
Target = A;
|
||||
Monster(Pawn).RangedAttack(Target);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO - base off land time rather than launch time?
|
||||
if((LastPounceTime + (4.5 - (FRand() * 3.0))) < Level.TimeSeconds )
|
||||
{
|
||||
aFacing=Normal(Vector(Pawn.Rotation));
|
||||
// Get the vector from A to B
|
||||
aToB=A.Location-Pawn.Location;
|
||||
|
||||
RelativeDir = aFacing dot aToB;
|
||||
if ( RelativeDir > 0.85 )
|
||||
{
|
||||
//Facing enemy
|
||||
if(IsInPounceDist(A) )
|
||||
{
|
||||
if(NiceZombieCrawler(Pawn).DoPounce()==true )
|
||||
LastPounceTime = Level.TimeSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function bool NotifyLanded(vector HitNormal)
|
||||
{
|
||||
if( NiceZombieCrawler(pawn).bPouncing )
|
||||
{
|
||||
// restart pathfinding from landing location
|
||||
GotoState('hunting');
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return super.NotifyLanded(HitNormal);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,97 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieFleshpoundBase extends NiceMonster
|
||||
abstract;
|
||||
var () float BlockDamageReduction;
|
||||
var bool bChargingPlayer,bClientCharge;
|
||||
var int TwoSecondDamageTotal;
|
||||
var float LastDamagedTime,RageEndTime;
|
||||
var() vector RotMag; // how far to rot view
|
||||
var() vector RotRate; // how fast to rot view
|
||||
var() float RotTime; // how much time to rot the instigator's view
|
||||
var() vector OffsetMag; // max view offset vertically
|
||||
var() vector OffsetRate; // how fast to offset view vertically
|
||||
var() float OffsetTime; // how much time to offset view
|
||||
var name ChargingAnim; // How he runs when charging the player.
|
||||
//var ONSHeadlightCorona DeviceGlow; //KFTODO: Don't think this is needed, its not reffed anywhere
|
||||
var() int RageDamageThreshold; // configurable.
|
||||
var NiceAvoidMarkerFP AvoidArea; // Make the other AI fear this AI
|
||||
var bool bFrustrated; // The fleshpound is tired of being kited and is pissed and ready to attack
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bChargingPlayer, bFrustrated;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
BlockDamageReduction=0.400000
RotMag=(X=500.000000,Y=500.000000,Z=600.000000)
RotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
RotTime=6.000000
OffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
OffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
OffsetTime=3.500000
ChargingAnim="PoundRun"
RageDamageThreshold=360
StunThreshold=4.000000
fuelRatio=0.150000
MeleeAnims(0)="PoundAttack1"
MeleeAnims(1)="PoundAttack2"
MeleeAnims(2)="PoundAttack3"
StunsRemaining=1
BleedOutDuration=7.000000
ZapThreshold=1.750000
ZappedDamageMod=1.250000
bHarpoonToBodyStuns=False
ZombieFlag=3
MeleeDamage=35
damageForce=15000
bFatAss=True
KFRagdollName="FleshPound_Trip"
SpinDamConst=20.000000
SpinDamRand=20.000000
bMeleeStunImmune=True
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=36.000000
ColHeight=35.000000
SeveredArmAttachScale=1.300000
SeveredLegAttachScale=1.200000
SeveredHeadAttachScale=1.500000
PlayerCountHealthScale=0.250000
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
OnlineHeadshotScale=1.300000
HeadHealth=700.000000
PlayerNumHeadHealthScale=0.300000
MotionDetectorThreat=5.000000
bBoss=True
ScoringValue=200
IdleHeavyAnim="PoundIdle"
IdleRifleAnim="PoundIdle"
RagDeathUpKick=100.000000
MeleeRange=55.000000
GroundSpeed=130.000000
WaterSpeed=120.000000
HealthMax=1650.000000
Health=1650
HeadHeight=2.500000
HeadScale=1.300000
MenuName="Nice Flesh Pound"
MovementAnims(0)="PoundWalk"
MovementAnims(1)="WalkB"
WalkAnims(0)="PoundWalk"
WalkAnims(1)="WalkB"
WalkAnims(2)="RunL"
WalkAnims(3)="RunR"
IdleCrouchAnim="PoundIdle"
IdleWeaponAnim="PoundIdle"
IdleRestAnim="PoundIdle"
PrePivot=(Z=0.000000)
Skins(1)=Shader'KFCharacters.FPAmberBloomShader'
Mass=600.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieFleshpoundBase extends NiceMonster
|
||||
abstract;
|
||||
var () float BlockDamageReduction;
|
||||
var bool bChargingPlayer,bClientCharge;
|
||||
var int TwoSecondDamageTotal;
|
||||
var float LastDamagedTime,RageEndTime;
|
||||
var() vector RotMag; // how far to rot view
|
||||
var() vector RotRate; // how fast to rot view
|
||||
var() float RotTime; // how much time to rot the instigator's view
|
||||
var() vector OffsetMag; // max view offset vertically
|
||||
var() vector OffsetRate; // how fast to offset view vertically
|
||||
var() float OffsetTime; // how much time to offset view
|
||||
var name ChargingAnim; // How he runs when charging the player.
|
||||
//var ONSHeadlightCorona DeviceGlow; //KFTODO: Don't think this is needed, its not reffed anywhere
|
||||
var() int RageDamageThreshold; // configurable.
|
||||
var NiceAvoidMarkerFP AvoidArea; // Make the other AI fear this AI
|
||||
var bool bFrustrated; // The fleshpound is tired of being kited and is pissed and ready to attack
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
|
||||
bChargingPlayer, bFrustrated;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
BlockDamageReduction=0.400000
|
||||
RotMag=(X=500.000000,Y=500.000000,Z=600.000000)
|
||||
RotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000)
|
||||
RotTime=6.000000
|
||||
OffsetMag=(X=5.000000,Y=10.000000,Z=5.000000)
|
||||
OffsetRate=(X=300.000000,Y=300.000000,Z=300.000000)
|
||||
OffsetTime=3.500000
|
||||
ChargingAnim="PoundRun"
|
||||
RageDamageThreshold=360
|
||||
StunThreshold=4.000000
|
||||
fuelRatio=0.150000
|
||||
MeleeAnims(0)="PoundAttack1"
|
||||
MeleeAnims(1)="PoundAttack2"
|
||||
MeleeAnims(2)="PoundAttack3"
|
||||
StunsRemaining=1
|
||||
BleedOutDuration=7.000000
|
||||
ZapThreshold=1.750000
|
||||
ZappedDamageMod=1.250000
|
||||
bHarpoonToBodyStuns=False
|
||||
ZombieFlag=3
|
||||
MeleeDamage=35
|
||||
damageForce=15000
|
||||
bFatAss=True
|
||||
KFRagdollName="FleshPound_Trip"
|
||||
SpinDamConst=20.000000
|
||||
SpinDamRand=20.000000
|
||||
bMeleeStunImmune=True
|
||||
Intelligence=BRAINS_Mammal
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=52.000000)
|
||||
ColRadius=36.000000
|
||||
ColHeight=35.000000
|
||||
SeveredArmAttachScale=1.300000
|
||||
SeveredLegAttachScale=1.200000
|
||||
SeveredHeadAttachScale=1.500000
|
||||
PlayerCountHealthScale=0.250000
|
||||
OnlineHeadshotOffset=(X=22.000000,Z=68.000000)
|
||||
OnlineHeadshotScale=1.300000
|
||||
HeadHealth=700.000000
|
||||
PlayerNumHeadHealthScale=0.300000
|
||||
MotionDetectorThreat=5.000000
|
||||
bBoss=True
|
||||
ScoringValue=200
|
||||
IdleHeavyAnim="PoundIdle"
|
||||
IdleRifleAnim="PoundIdle"
|
||||
RagDeathUpKick=100.000000
|
||||
MeleeRange=55.000000
|
||||
GroundSpeed=130.000000
|
||||
WaterSpeed=120.000000
|
||||
HealthMax=1650.000000
|
||||
Health=1650
|
||||
HeadHeight=2.500000
|
||||
HeadScale=1.300000
|
||||
MenuName="Nice Flesh Pound"
|
||||
MovementAnims(0)="PoundWalk"
|
||||
MovementAnims(1)="WalkB"
|
||||
WalkAnims(0)="PoundWalk"
|
||||
WalkAnims(1)="WalkB"
|
||||
WalkAnims(2)="RunL"
|
||||
WalkAnims(3)="RunR"
|
||||
IdleCrouchAnim="PoundIdle"
|
||||
IdleWeaponAnim="PoundIdle"
|
||||
IdleRestAnim="PoundIdle"
|
||||
PrePivot=(Z=0.000000)
|
||||
Skins(1)=Shader'KFCharacters.FPAmberBloomShader'
|
||||
Mass=600.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,116 +1,197 @@
|
|||
class NiceZombieFleshpoundController extends NiceMonsterController;
|
||||
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var bool bDoneSpottedCheck;
|
||||
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
|
||||
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
|
||||
simulated function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
RageFrustrationTimer = 0;
|
||||
}
|
||||
function Tick(float Delta){
|
||||
local bool bSeesPlayers;
|
||||
local Controller PC;
|
||||
local KFHumanPawn Human;
|
||||
local NiceZombieFleshPound ZFP;
|
||||
super.Tick(Delta);
|
||||
bSeesPlayers = false;
|
||||
for(PC = Level.ControllerList;PC != none;PC = PC.NextController){
Human = KFHumanPawn(PC.Pawn);
if(Human != none && Human.Health > 0 && !Human.bPendingDelete && CanSee(Human)){
bSeesPlayers = true;
break;
}
|
||||
}
|
||||
if(bSeesPlayers){
if(RageFrustrationTimer < RageFrustrationThreshhold){
RageFrustrationTimer += Delta;
if(RageFrustrationTimer >= RageFrustrationThreshhold){
ZFP = NiceZombieFleshPound(Pawn);
if(ZFP != none && !ZFP.bChargingPlayer){
ZFP.StartChargingFP(Pawn(focus));
ZFP.bFrustrated = true;
}
}
}
|
||||
}
|
||||
else
RageFrustrationTimer = 0;
|
||||
}
|
||||
// Never do that, you too cool
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Fleshpound saying something
if ( !KFGameType(Level.Game).bDidSpottedFleshpoundMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 12, "");
KFGameType(Level.Game).bDidSpottedFleshpoundMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else
SetTimer(0.01, True);
|
||||
}
|
||||
state SpinAttack
|
||||
{
|
||||
ignores EnemyNotVisible;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function DoSpinDamage()
|
||||
{
local Actor A;
|
||||
//log("FLESHPOUND DOSPINDAMAGE!");
foreach CollidingActors(class'actor', A, (NiceZombieFleshpound(pawn).MeleeRange * 1.5)+pawn.CollisionRadius, pawn.Location)
NiceZombieFleshpound(pawn).SpinDamage(A);
|
||||
}
|
||||
Begin:
|
||||
WaitForAnim:
|
||||
While( KFM.bShotAnim )
|
||||
{
Sleep(0.1);
DoSpinDamage();
|
||||
}
|
||||
WhatToDoNext(152);
|
||||
if ( bSoaking )
SoakStop("STUCK IN SPINATTACK!!!");
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
// I suspect this function causes bloats to get confused
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
if ( Monster(Pawn).bShotAnim )
|
||||
{
Goto('Moving');
|
||||
}
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
|
||||
// for the FleshPound getting stuck in its idle anim on a dedicated server when it
|
||||
// is supposed to be raging. For some reason, on a dedicated server only, it
|
||||
// never gets an animend call for the PoundRage anim, instead the anim gets
|
||||
// interrupted by the PoundIdle anim. If we figure that bug out, we can
|
||||
// probably take this out in the future. But for now the fix works - Ramm
|
||||
function SetPoundRageTimout(float NewRageTimeOut)
|
||||
{
|
||||
RageAnimTimeout = NewRageTimeOut;
|
||||
}
|
||||
state WaitForAnim
|
||||
{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function BeginState()
|
||||
{
bUseFreezeHack = False;
|
||||
}
|
||||
// The rage anim has ended, clear the flags and let the AI do its thing
|
||||
function RageTimeout()
|
||||
{
if( bUseFreezeHack )
{
if( Pawn!=none )
{
Pawn.AccelRate = Pawn.Default.AccelRate;
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
}
bUseFreezeHack = False;
AnimEnd(0);
}
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
Global.Tick(Delta);
|
||||
if( RageAnimTimeout > 0 )
{
RageAnimTimeout -= Delta;
|
||||
if( RageAnimTimeout <= 0 )
{
RageAnimTimeout = 0;
RageTimeout();
}
}
|
||||
if( bUseFreezeHack )
{
MoveTarget = none;
MoveTimer = -1;
Pawn.Acceleration = vect(0,0,0);
Pawn.GroundSpeed = 1;
Pawn.AccelRate = 0;
}
|
||||
}
|
||||
function EndState()
|
||||
{
if( Pawn!=none )
{
Pawn.AccelRate = Pawn.Default.AccelRate;
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
}
bUseFreezeHack = False;
|
||||
}
|
||||
Begin:
|
||||
While( KFM.bShotAnim )
|
||||
{
Sleep(0.15);
|
||||
}
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
defaultproperties
|
||||
{
RageFrustrationThreshhold=10.000000
|
||||
}
|
||||
class NiceZombieFleshpoundController extends NiceMonsterController;
|
||||
var float RageAnimTimeout; // How long until the RageAnim is completed; Hack so the server doesn't get stuck in idle when its doing the Rage anim
|
||||
var bool bDoneSpottedCheck;
|
||||
var float RageFrustrationTimer; // Tracks how long we have been walking toward a visible enemy
|
||||
var float RageFrustrationThreshhold; // Base value for how long the FP should walk torward an enemy without reaching them before getting frustrated and raging
|
||||
simulated function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
RageFrustrationTimer = 0;
|
||||
}
|
||||
function Tick(float Delta){
|
||||
local bool bSeesPlayers;
|
||||
local Controller PC;
|
||||
local KFHumanPawn Human;
|
||||
local NiceZombieFleshPound ZFP;
|
||||
super.Tick(Delta);
|
||||
bSeesPlayers = false;
|
||||
for(PC = Level.ControllerList;PC != none;PC = PC.NextController){
|
||||
Human = KFHumanPawn(PC.Pawn);
|
||||
if(Human != none && Human.Health > 0 && !Human.bPendingDelete && CanSee(Human)){
|
||||
bSeesPlayers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(bSeesPlayers){
|
||||
if(RageFrustrationTimer < RageFrustrationThreshhold){
|
||||
RageFrustrationTimer += Delta;
|
||||
if(RageFrustrationTimer >= RageFrustrationThreshhold){
|
||||
ZFP = NiceZombieFleshPound(Pawn);
|
||||
if(ZFP != none && !ZFP.bChargingPlayer){
|
||||
ZFP.StartChargingFP(Pawn(focus));
|
||||
ZFP.bFrustrated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
RageFrustrationTimer = 0;
|
||||
}
|
||||
// Never do that, you too cool
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
|
||||
{
|
||||
// 25% chance of first player to see this Fleshpound saying something
|
||||
if ( !KFGameType(Level.Game).bDidSpottedFleshpoundMessage && FRand() < 0.25 )
|
||||
{
|
||||
PlayerController(SeenPlayer.Controller).Speech('AUTO', 12, "");
|
||||
KFGameType(Level.Game).bDidSpottedFleshpoundMessage = true;
|
||||
}
|
||||
|
||||
bDoneSpottedCheck = true;
|
||||
}
|
||||
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
|
||||
SetCombatTimer();
|
||||
else
|
||||
SetTimer(0.01, True);
|
||||
}
|
||||
state SpinAttack
|
||||
{
|
||||
ignores EnemyNotVisible;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function DoSpinDamage()
|
||||
{
|
||||
local Actor A;
|
||||
|
||||
//log("FLESHPOUND DOSPINDAMAGE!");
|
||||
foreach CollidingActors(class'actor', A, (NiceZombieFleshpound(pawn).MeleeRange * 1.5)+pawn.CollisionRadius, pawn.Location)
|
||||
NiceZombieFleshpound(pawn).SpinDamage(A);
|
||||
}
|
||||
Begin:
|
||||
WaitForAnim:
|
||||
While( KFM.bShotAnim )
|
||||
{
|
||||
Sleep(0.1);
|
||||
DoSpinDamage();
|
||||
}
|
||||
WhatToDoNext(152);
|
||||
if ( bSoaking )
|
||||
SoakStop("STUCK IN SPINATTACK!!!");
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// I suspect this function causes bloats to get confused
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
|
||||
Disable('NotifyBump');
|
||||
Target = Enemy;
|
||||
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
if ( Monster(Pawn).bShotAnim )
|
||||
{
|
||||
Goto('Moving');
|
||||
}
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
|
||||
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
|
||||
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
// Used to set a timeout for the WaitForAnim state. This is a bit of a hack fix
|
||||
// for the FleshPound getting stuck in its idle anim on a dedicated server when it
|
||||
// is supposed to be raging. For some reason, on a dedicated server only, it
|
||||
// never gets an animend call for the PoundRage anim, instead the anim gets
|
||||
// interrupted by the PoundIdle anim. If we figure that bug out, we can
|
||||
// probably take this out in the future. But for now the fix works - Ramm
|
||||
function SetPoundRageTimout(float NewRageTimeOut)
|
||||
{
|
||||
RageAnimTimeout = NewRageTimeOut;
|
||||
}
|
||||
state WaitForAnim
|
||||
{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function BeginState()
|
||||
{
|
||||
bUseFreezeHack = False;
|
||||
}
|
||||
// The rage anim has ended, clear the flags and let the AI do its thing
|
||||
function RageTimeout()
|
||||
{
|
||||
if( bUseFreezeHack )
|
||||
{
|
||||
if( Pawn!=none )
|
||||
{
|
||||
Pawn.AccelRate = Pawn.Default.AccelRate;
|
||||
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
|
||||
}
|
||||
bUseFreezeHack = False;
|
||||
AnimEnd(0);
|
||||
}
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
Global.Tick(Delta);
|
||||
|
||||
if( RageAnimTimeout > 0 )
|
||||
{
|
||||
RageAnimTimeout -= Delta;
|
||||
|
||||
if( RageAnimTimeout <= 0 )
|
||||
{
|
||||
RageAnimTimeout = 0;
|
||||
RageTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
if( bUseFreezeHack )
|
||||
{
|
||||
MoveTarget = none;
|
||||
MoveTimer = -1;
|
||||
Pawn.Acceleration = vect(0,0,0);
|
||||
Pawn.GroundSpeed = 1;
|
||||
Pawn.AccelRate = 0;
|
||||
}
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
if( Pawn!=none )
|
||||
{
|
||||
Pawn.AccelRate = Pawn.Default.AccelRate;
|
||||
Pawn.GroundSpeed = Pawn.Default.GroundSpeed;
|
||||
}
|
||||
bUseFreezeHack = False;
|
||||
}
|
||||
Begin:
|
||||
While( KFM.bShotAnim )
|
||||
{
|
||||
Sleep(0.15);
|
||||
}
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
RageFrustrationThreshhold=10.000000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,139 +1,242 @@
|
|||
// Completely invisible Stalker
|
||||
// (c) PooSH, 2014
|
||||
// used 'ClawAndMove' code from Scary Ghost's SuperStaler
|
||||
// Ported to 'NiceMonster' parent class
|
||||
class NiceZombieGhost extends NiceZombieStalker;
|
||||
// max distance squared for player to see cloacked Stalkers.
|
||||
// Beyond that distance Stalkers will appear completely invisible.
|
||||
var float CloakDistanceSqr;
|
||||
// Unclock distance squared for Commandos
|
||||
var float UncloakDistanceSqr;
|
||||
var const Material CloakMat;
|
||||
var const Material InvisibleMat;
|
||||
var const Material UncloakMat, UncloakFBMat;
|
||||
var const Material GlowFX;
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(default.CloakMat);
|
||||
myLevel.AddPrecacheMaterial(default.InvisibleMat);
|
||||
myLevel.AddPrecacheMaterial(default.UncloakMat);
|
||||
myLevel.AddPrecacheMaterial(default.UncloakFBMat);
|
||||
myLevel.AddPrecacheMaterial(default.GlowFX);
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
|
||||
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
super.PostNetBeginPlay();
|
||||
if ( LocalKFHumanPawn != none ) {
CloakDistanceSqr = fmax(CloakDistanceSqr, 3.0 * UncloakDistanceSqr * LocalKFHumanPawn.GetStalkerViewDistanceMulti());
UncloakDistanceSqr *= LocalKFHumanPawn.GetStalkerViewDistanceMulti();
|
||||
}
|
||||
}
|
||||
|
||||
function RenderOverlays(Canvas Canvas)
|
||||
{
|
||||
Canvas.SetDrawColor(0, 92, 255, 255);
|
||||
super.RenderOverlays(Canvas);
|
||||
}
|
||||
// makes Stalker invisible or glowing (for commando)
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if ( bSpotted ) {
if( Level.NetMode == NM_DedicatedServer )
return;
|
||||
Skins[0] = GlowFX;
Skins[1] = GlowFX;
bUnlit = true;
|
||||
}
|
||||
else if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
|
||||
{
Visibility = 1;
bCloaked = true;
|
||||
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
Skins[0] = InvisibleMat;
Skins[1] = InvisibleMat;
bUnlit = false;
|
||||
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
|
||||
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
LastUncloakTime = Level.TimeSeconds;
|
||||
Visibility = default.Visibility;
bCloaked = false;
bUnlit = false;
|
||||
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
if ( Skins[0] != UncloakMat )
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = UncloakMat;
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super(NiceMonster).RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = UncloakFBMat;
Skins[0] = UncloakMat;
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super(NiceMonster).PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = UncloakFBMat;
Skins[0] = UncloakMat;
|
||||
}
|
||||
}
|
||||
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
local float DistanceSqr;
|
||||
Super(NiceMonster).Tick(DeltaTime);
|
||||
// Keep the stalker moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim && !bZapped ) {
if( LookTarget!=none ) {
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
|
||||
}
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
|
||||
if( bZapped ) {
// Make sure we check if we need to be cloaked as soon as the zap wears off
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
bSpotted = false;
if ( LocalKFHumanPawn != none ) {
DistanceSqr = VSizeSquared(Location - LocalKFHumanPawn.Location);
if( LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers()
&& DistanceSqr < UncloakDistanceSqr )
{
bSpotted = True;
if ( Skins[0] != GlowFX ) {
Skins[0] = GlowFX;
Skins[1] = GlowFX;
bUnlit = true; }
}
else if ( DistanceSqr < CloakDistanceSqr ) {
if ( bCloaked && Skins[0] != CloakMat ) {
Skins[0] = CloakMat;
Skins[1] = CloakMat;
bUnlit = false;
}
}
else if ( Skins[0] != InvisibleMat ) {
CloakStalker();
}
}
|
||||
}
|
||||
}
|
||||
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( !bShotAnim && Physics != PHYS_Swimming && CanAttack(A) ) {
bShotAnim = true;
SetAnimAction('ClawAndMove');
|
||||
}
|
||||
}
|
||||
// copied from ZombieSuperStalker (c) Scary Ghost
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if( NewAction=='' )
Return;
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
bWaitForAnim= false;
|
||||
if( Level.NetMode!=NM_Client ) {
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// copied from ZombieSuperStalker (c) Scary Ghost
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local float duration;
|
||||
if( AnimName == 'ClawAndMove' ) {
meleeAnimIndex = Rand(3);
AnimName = meleeAnims[meleeAnimIndex];
|
||||
duration= GetAnimDuration(AnimName, 1.0);
|
||||
}
|
||||
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') {
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
defaultproperties
|
||||
{
CloakDistanceSqr=62500.000000
UncloakDistanceSqr=360000.000000
CloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
InvisibleMat=Shader'KF_Specimens_Trip_T.patriarch_invisible'
UncloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
UncloakFBMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
MenuName="Ghost"
Skins(0)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
Skins(1)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
|
||||
}
|
||||
// Completely invisible Stalker
|
||||
// (c) PooSH, 2014
|
||||
// used 'ClawAndMove' code from Scary Ghost's SuperStaler
|
||||
// Ported to 'NiceMonster' parent class
|
||||
class NiceZombieGhost extends NiceZombieStalker;
|
||||
// max distance squared for player to see cloacked Stalkers.
|
||||
// Beyond that distance Stalkers will appear completely invisible.
|
||||
var float CloakDistanceSqr;
|
||||
// Unclock distance squared for Commandos
|
||||
var float UncloakDistanceSqr;
|
||||
var const Material CloakMat;
|
||||
var const Material InvisibleMat;
|
||||
var const Material UncloakMat, UncloakFBMat;
|
||||
var const Material GlowFX;
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{
|
||||
myLevel.AddPrecacheMaterial(default.CloakMat);
|
||||
myLevel.AddPrecacheMaterial(default.InvisibleMat);
|
||||
myLevel.AddPrecacheMaterial(default.UncloakMat);
|
||||
myLevel.AddPrecacheMaterial(default.UncloakFBMat);
|
||||
myLevel.AddPrecacheMaterial(default.GlowFX);
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
|
||||
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
super.PostNetBeginPlay();
|
||||
if ( LocalKFHumanPawn != none ) {
|
||||
CloakDistanceSqr = fmax(CloakDistanceSqr, 3.0 * UncloakDistanceSqr * LocalKFHumanPawn.GetStalkerViewDistanceMulti());
|
||||
UncloakDistanceSqr *= LocalKFHumanPawn.GetStalkerViewDistanceMulti();
|
||||
}
|
||||
}
|
||||
|
||||
function RenderOverlays(Canvas Canvas)
|
||||
{
|
||||
Canvas.SetDrawColor(0, 92, 255, 255);
|
||||
super.RenderOverlays(Canvas);
|
||||
}
|
||||
// makes Stalker invisible or glowing (for commando)
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( bSpotted ) {
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
return;
|
||||
|
||||
Skins[0] = GlowFX;
|
||||
Skins[1] = GlowFX;
|
||||
bUnlit = true;
|
||||
}
|
||||
else if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
|
||||
{
|
||||
Visibility = 1;
|
||||
bCloaked = true;
|
||||
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
Return;
|
||||
|
||||
Skins[0] = InvisibleMat;
|
||||
Skins[1] = InvisibleMat;
|
||||
bUnlit = false;
|
||||
|
||||
// Invisible - no shadow
|
||||
if(PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = false;
|
||||
if(RealTimeShadow != none)
|
||||
RealTimeShadow.Destroy();
|
||||
|
||||
// Remove/disallow projectors on invisible people
|
||||
Projectors.Remove(0, Projectors.Length);
|
||||
bAcceptsProjectors = false;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
|
||||
LastUncloakTime = Level.TimeSeconds;
|
||||
|
||||
Visibility = default.Visibility;
|
||||
bCloaked = false;
|
||||
bUnlit = false;
|
||||
|
||||
// 25% chance of our Enemy saying something about us being invisible
|
||||
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
|
||||
PlayerController(Controller.Enemy.Controller)!=none )
|
||||
{
|
||||
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
|
||||
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
|
||||
}
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
Return;
|
||||
|
||||
if ( Skins[0] != UncloakMat )
|
||||
{
|
||||
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
|
||||
Skins[0] = UncloakMat;
|
||||
|
||||
if (PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = true;
|
||||
|
||||
bAcceptsProjectors = true;
|
||||
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super(NiceMonster).RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
|
||||
Skins[1] = UncloakFBMat;
|
||||
Skins[0] = UncloakMat;
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super(NiceMonster).PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
|
||||
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
|
||||
Skins[1] = UncloakFBMat;
|
||||
Skins[0] = UncloakMat;
|
||||
}
|
||||
}
|
||||
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
local float DistanceSqr;
|
||||
Super(NiceMonster).Tick(DeltaTime);
|
||||
// Keep the stalker moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim && !bZapped ) {
|
||||
if( LookTarget!=none ) {
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
if( Level.NetMode==NM_DedicatedServer )
|
||||
Return; // Servers aren't intrested in this info.
|
||||
if( bZapped ) {
|
||||
// Make sure we check if we need to be cloaked as soon as the zap wears off
|
||||
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
|
||||
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
|
||||
bSpotted = false;
|
||||
if ( LocalKFHumanPawn != none ) {
|
||||
DistanceSqr = VSizeSquared(Location - LocalKFHumanPawn.Location);
|
||||
if( LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers()
|
||||
&& DistanceSqr < UncloakDistanceSqr )
|
||||
{
|
||||
bSpotted = True;
|
||||
if ( Skins[0] != GlowFX ) {
|
||||
Skins[0] = GlowFX;
|
||||
Skins[1] = GlowFX;
|
||||
bUnlit = true; }
|
||||
}
|
||||
else if ( DistanceSqr < CloakDistanceSqr ) {
|
||||
if ( bCloaked && Skins[0] != CloakMat ) {
|
||||
Skins[0] = CloakMat;
|
||||
Skins[1] = CloakMat;
|
||||
bUnlit = false;
|
||||
}
|
||||
}
|
||||
else if ( Skins[0] != InvisibleMat ) {
|
||||
CloakStalker();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( !bShotAnim && Physics != PHYS_Swimming && CanAttack(A) ) {
|
||||
bShotAnim = true;
|
||||
SetAnimAction('ClawAndMove');
|
||||
}
|
||||
}
|
||||
// copied from ZombieSuperStalker (c) Scary Ghost
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
bWaitForAnim= false;
|
||||
if( Level.NetMode!=NM_Client ) {
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// copied from ZombieSuperStalker (c) Scary Ghost
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local float duration;
|
||||
if( AnimName == 'ClawAndMove' ) {
|
||||
meleeAnimIndex = Rand(3);
|
||||
AnimName = meleeAnims[meleeAnimIndex];
|
||||
|
||||
duration= GetAnimDuration(AnimName, 1.0);
|
||||
}
|
||||
if( AnimName=='StalkerSpinAttack' || AnimName=='StalkerAttack1' || AnimName=='JumpAttack') {
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
CloakDistanceSqr=62500.000000
|
||||
UncloakDistanceSqr=360000.000000
|
||||
CloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
InvisibleMat=Shader'KF_Specimens_Trip_T.patriarch_invisible'
|
||||
UncloakMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
UncloakFBMat=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
MenuName="Ghost"
|
||||
Skins(0)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
|
||||
Skins(1)=Shader'KF_Specimens_Trip_T.patriarch_invisible'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,63 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
|
||||
class NiceZombieGoreFastBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var bool bRunning;
|
||||
var float RunAttackTimeout;
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bRunning;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
fuelRatio=0.650000
clientHeadshotScale=1.600000
MeleeAnims(0)="GoreAttack1"
MeleeAnims(1)="GoreAttack2"
MeleeAnims(2)="GoreAttack1"
bCannibal=True
MeleeDamage=15
damageForce=5000
KFRagdollName="GoreFast_Trip"
CrispUpThreshhold=8
bUseExtendedCollision=True
ColOffset=(Z=52.000000)
ColRadius=25.000000
ColHeight=10.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.900000
SeveredLegAttachScale=0.900000
PlayerCountHealthScale=0.150000
OnlineHeadshotOffset=(X=5.000000,Z=53.000000)
OnlineHeadshotScale=1.500000
MotionDetectorThreat=0.500000
ScoringValue=12
IdleHeavyAnim="GoreIdle"
IdleRifleAnim="GoreIdle"
MeleeRange=30.000000
GroundSpeed=120.000000
WaterSpeed=140.000000
HealthMax=250.000000
Health=250
HeadHeight=2.500000
HeadScale=1.500000
MenuName="Nice Gorefast"
MovementAnims(0)="GoreWalk"
WalkAnims(0)="GoreWalk"
WalkAnims(1)="GoreWalk"
WalkAnims(2)="GoreWalk"
WalkAnims(3)="GoreWalk"
IdleCrouchAnim="GoreIdle"
IdleWeaponAnim="GoreIdle"
IdleRestAnim="GoreIdle"
DrawScale=1.200000
PrePivot=(Z=10.000000)
Mass=350.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
|
||||
class NiceZombieGoreFastBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var bool bRunning;
|
||||
var float RunAttackTimeout;
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
|
||||
bRunning;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
fuelRatio=0.650000
|
||||
clientHeadshotScale=1.600000
|
||||
MeleeAnims(0)="GoreAttack1"
|
||||
MeleeAnims(1)="GoreAttack2"
|
||||
MeleeAnims(2)="GoreAttack1"
|
||||
bCannibal=True
|
||||
MeleeDamage=15
|
||||
damageForce=5000
|
||||
KFRagdollName="GoreFast_Trip"
|
||||
CrispUpThreshhold=8
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=52.000000)
|
||||
ColRadius=25.000000
|
||||
ColHeight=10.000000
|
||||
ExtCollAttachBoneName="Collision_Attach"
|
||||
SeveredArmAttachScale=0.900000
|
||||
SeveredLegAttachScale=0.900000
|
||||
PlayerCountHealthScale=0.150000
|
||||
OnlineHeadshotOffset=(X=5.000000,Z=53.000000)
|
||||
OnlineHeadshotScale=1.500000
|
||||
MotionDetectorThreat=0.500000
|
||||
ScoringValue=12
|
||||
IdleHeavyAnim="GoreIdle"
|
||||
IdleRifleAnim="GoreIdle"
|
||||
MeleeRange=30.000000
|
||||
GroundSpeed=120.000000
|
||||
WaterSpeed=140.000000
|
||||
HealthMax=250.000000
|
||||
Health=250
|
||||
HeadHeight=2.500000
|
||||
HeadScale=1.500000
|
||||
MenuName="Nice Gorefast"
|
||||
MovementAnims(0)="GoreWalk"
|
||||
WalkAnims(0)="GoreWalk"
|
||||
WalkAnims(1)="GoreWalk"
|
||||
WalkAnims(2)="GoreWalk"
|
||||
WalkAnims(3)="GoreWalk"
|
||||
IdleCrouchAnim="GoreIdle"
|
||||
IdleWeaponAnim="GoreIdle"
|
||||
IdleRestAnim="GoreIdle"
|
||||
DrawScale=1.200000
|
||||
PrePivot=(Z=10.000000)
|
||||
Mass=350.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,153 +1,307 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
// GOREFAST.
|
||||
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
|
||||
class NiceZombieGoreFast extends NiceZombieGoreFastBase;
|
||||
#exec OBJ LOAD FILE=
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostNetReceive(){
|
||||
if(bRunning)
MovementAnims[0] = 'ZombieRun';
|
||||
else
MovementAnims[0] = default.MovementAnims[0];
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
NumZCDHits++;
|
||||
// if we hit him a couple of times, make him rage!
if( NumZCDHits > 1 )
{
if( !IsInState('RunningToMarker') )
{
GotoState('RunningToMarker');
}
else
{
NumZCDHits = 1;
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
}
else
{
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
|
||||
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
|
||||
}
|
||||
else
|
||||
{
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bRunning && NumZCDHits > 1 )
|
||||
{
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
GotoState('');
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
super.RangedAttack(A);
|
||||
if(!bShotAnim && !bDecapitated && VSize(A.Location - Location) <= 700)
GoToState('RunningState');
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
super.Tick(DeltaTime);
|
||||
if(IsInState('RunningState'))
SetGroundSpeed(GetOriginalGroundSpeed() * 1.875);
|
||||
else
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
state RunningState{
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior(){
Global.SetZappedBehavior();
GoToState('');
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust(){
return false;
|
||||
}
|
||||
simulated function BeginState(){
if(bZapped)
GoToState('');
else{
SetGroundSpeed(OriginalGroundSpeed * 1.875);
bRunning = true;
if(Level.NetMode != NM_DedicatedServer)
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function EndState(){
SetGroundSpeed(GetOriginalGroundSpeed());
bRunning = false;
if(Level.NetMode != NM_DedicatedServer)
PostNetReceive();
|
||||
RunAttackTimeout=0;
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function RemoveHead(){
GoToState('');
Global.RemoveHead();
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
if(bShotAnim || Physics == PHYS_Swimming)
return;
else if (CanAttack(A)){
bShotAnim = true;
|
||||
// Randomly do a moving attack so the player can't kite the zed
if(FRand() < 0.4){
SetAnimAction('ClawAndMove');
RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0);
}
else{
SetAnimAction('Claw');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
// Once we attack stop running
GoToState('');
}
return;
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
// Keep moving toward the target until the timer runs out (anim finishes)
if(RunAttackTimeout > 0){
RunAttackTimeout -= DeltaTime;
|
||||
if(RunAttackTimeout <= 0 && !bZedUnderControl){
RunAttackTimeout = 0;
GoToState('');
}
}
|
||||
// Keep the gorefast moving toward its target when attacking
if(Role == ROLE_Authority && bShotAnim && !bWaitForAnim){
if(LookTarget != none)
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
global.Tick(DeltaTime);
|
||||
}
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if( Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700 )
|
||||
{
Sleep(0.5+ FRand() * 0.5);
//log("Still charging");
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
{
//log("Done charging");
GoToState('');
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
state RunningToMarker extends RunningState
|
||||
{
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
// Keep moving toward the target until the timer runs out (anim finishes)
if( RunAttackTimeout > 0 )
{
RunAttackTimeout -= DeltaTime;
|
||||
if( RunAttackTimeout <= 0 && !bZedUnderControl )
{
RunAttackTimeout = 0;
GoToState('');
}
}
|
||||
// Keep the gorefast moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim )
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
global.Tick(DeltaTime);
|
||||
}
|
||||
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if( bZedUnderControl || (Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700) )
|
||||
{
Sleep(0.5+ FRand() * 0.5);
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
{
GoToState('');
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ClawAndMove';
|
||||
if( NewAction == 'Claw' )
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( AnimName == 'ClawAndMove' )
|
||||
{
meleeAnimIndex = Rand(3);
AnimName = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( AnimName=='GoreAttack1' || AnimName=='GoreAttack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
// Gorefast does not have a left arm and does not need it to be hidden
|
||||
if (boneName != LeftFArmBone)
|
||||
{
super.HideBone(boneName);
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gorefast_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
stunLoopStart=0.287500
stunLoopEnd=0.637500
idleInsertFrame=0.750000
EventClasses(0)="NicePack.NiceZombieGorefast"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
DetachedArmClass=Class'KFChar.SeveredArmGorefast'
DetachedLegClass=Class'KFChar.SeveredLegGorefast'
DetachedHeadClass=Class'KFChar.SeveredHeadGorefast'
bLeftArmGibbed=True
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ControllerClass=Class'NicePack.NiceZombieGorefastController'
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.GoreFast_Freak'
Skins(0)=Combiner'KF_Specimens_Trip_T.gorefast_cmb'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
// GOREFAST.
|
||||
// He's speedy, and swings with a Single enlongated arm, affording him slightly more range
|
||||
class NiceZombieGoreFast extends NiceZombieGoreFastBase;
|
||||
#exec OBJ LOAD FILE=
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostNetReceive(){
|
||||
if(bRunning)
|
||||
MovementAnims[0] = 'ZombieRun';
|
||||
else
|
||||
MovementAnims[0] = default.MovementAnims[0];
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
|
||||
NumZCDHits++;
|
||||
|
||||
// if we hit him a couple of times, make him rage!
|
||||
if( NumZCDHits > 1 )
|
||||
{
|
||||
if( !IsInState('RunningToMarker') )
|
||||
{
|
||||
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
|
||||
NumZCDHits = 1;
|
||||
if( IsInState('RunningToMarker') )
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsInState('RunningToMarker') )
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
|
||||
if( bNewMindControlled != bZedUnderControl )
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 1.25);
|
||||
Health *= 1.25;
|
||||
HealthMax *= 1.25;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bRunning && NumZCDHits > 1 )
|
||||
{
|
||||
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
super.RangedAttack(A);
|
||||
if(!bShotAnim && !bDecapitated && VSize(A.Location - Location) <= 700)
|
||||
GoToState('RunningState');
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
super.Tick(DeltaTime);
|
||||
if(IsInState('RunningState'))
|
||||
SetGroundSpeed(GetOriginalGroundSpeed() * 1.875);
|
||||
else
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
state RunningState{
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior(){
|
||||
Global.SetZappedBehavior();
|
||||
GoToState('');
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust(){
|
||||
return false;
|
||||
}
|
||||
simulated function BeginState(){
|
||||
if(bZapped)
|
||||
GoToState('');
|
||||
else{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 1.875);
|
||||
bRunning = true;
|
||||
if(Level.NetMode != NM_DedicatedServer)
|
||||
PostNetReceive();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
function EndState(){
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
bRunning = false;
|
||||
if(Level.NetMode != NM_DedicatedServer)
|
||||
PostNetReceive();
|
||||
|
||||
RunAttackTimeout=0;
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
function RemoveHead(){
|
||||
GoToState('');
|
||||
Global.RemoveHead();
|
||||
}
|
||||
function RangedAttack(Actor A){
|
||||
|
||||
if(bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if (CanAttack(A)){
|
||||
bShotAnim = true;
|
||||
|
||||
// Randomly do a moving attack so the player can't kite the zed
|
||||
if(FRand() < 0.4){
|
||||
SetAnimAction('ClawAndMove');
|
||||
RunAttackTimeout = GetAnimDuration('GoreAttack1', 1.0);
|
||||
}
|
||||
else{
|
||||
SetAnimAction('Claw');
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
// Once we attack stop running
|
||||
GoToState('');
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
// Keep moving toward the target until the timer runs out (anim finishes)
|
||||
if(RunAttackTimeout > 0){
|
||||
RunAttackTimeout -= DeltaTime;
|
||||
|
||||
if(RunAttackTimeout <= 0 && !bZedUnderControl){
|
||||
RunAttackTimeout = 0;
|
||||
GoToState('');
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the gorefast moving toward its target when attacking
|
||||
if(Role == ROLE_Authority && bShotAnim && !bWaitForAnim){
|
||||
if(LookTarget != none)
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
global.Tick(DeltaTime);
|
||||
}
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if( Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700 )
|
||||
{
|
||||
Sleep(0.5+ FRand() * 0.5);
|
||||
//log("Still charging");
|
||||
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
{
|
||||
//log("Done charging");
|
||||
GoToState('');
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
state RunningToMarker extends RunningState
|
||||
{
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
// Keep moving toward the target until the timer runs out (anim finishes)
|
||||
if( RunAttackTimeout > 0 )
|
||||
{
|
||||
RunAttackTimeout -= DeltaTime;
|
||||
|
||||
if( RunAttackTimeout <= 0 && !bZedUnderControl )
|
||||
{
|
||||
RunAttackTimeout = 0;
|
||||
GoToState('');
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the gorefast moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim )
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
|
||||
global.Tick(DeltaTime);
|
||||
}
|
||||
|
||||
Begin:
|
||||
GoTo('CheckCharge');
|
||||
CheckCharge:
|
||||
if( bZedUnderControl || (Controller!=none && Controller.Target!=none && VSize(Controller.Target.Location-Location)<700) )
|
||||
{
|
||||
Sleep(0.5+ FRand() * 0.5);
|
||||
GoTo('CheckCharge');
|
||||
}
|
||||
else
|
||||
{
|
||||
GoToState('');
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ClawAndMove';
|
||||
if( NewAction == 'Claw' )
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( AnimName == 'ClawAndMove' )
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
AnimName = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( AnimName=='GoreAttack1' || AnimName=='GoreAttack2' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
simulated function HideBone(name boneName)
|
||||
{
|
||||
// Gorefast does not have a left arm and does not need it to be hidden
|
||||
if (boneName != LeftFArmBone)
|
||||
{
|
||||
super.HideBone(boneName);
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.gorefast_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.gorefast_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
stunLoopStart=0.287500
|
||||
stunLoopEnd=0.637500
|
||||
idleInsertFrame=0.750000
|
||||
EventClasses(0)="NicePack.NiceZombieGorefast"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmGorefast'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegGorefast'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadGorefast'
|
||||
bLeftArmGibbed=True
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ControllerClass=Class'NicePack.NiceZombieGorefastController'
|
||||
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.GoreFast_Freak'
|
||||
Skins(0)=Combiner'KF_Specimens_Trip_T.gorefast_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
// Custom code to make the Gorefast act abit more interesting.
|
||||
class NiceZombieGorefastController extends NiceMonsterController;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt{
|
||||
event SeePlayer(Pawn SeenPlayer){
if(!bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none){
// 25% chance of first player to see this Gorefast saying something
if(!KFGameType(Level.Game).bDidSpottedGorefastMessage && FRand() < 0.25){
PlayerController(SeenPlayer.Controller).Speech('AUTO', 13, "");
KFGameType(Level.Game).bDidSpottedGorefastMessage = true;
}
bDoneSpottedCheck = true;
}
global.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
StrafingAbility=0.500000
|
||||
}
|
||||
// Custom code to make the Gorefast act abit more interesting.
|
||||
class NiceZombieGorefastController extends NiceMonsterController;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt{
|
||||
event SeePlayer(Pawn SeenPlayer){
|
||||
if(!bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none){
|
||||
// 25% chance of first player to see this Gorefast saying something
|
||||
if(!KFGameType(Level.Game).bDidSpottedGorefastMessage && FRand() < 0.25){
|
||||
PlayerController(SeenPlayer.Controller).Speech('AUTO', 13, "");
|
||||
KFGameType(Level.Game).bDidSpottedGorefastMessage = true;
|
||||
}
|
||||
bDoneSpottedCheck = true;
|
||||
}
|
||||
global.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
StrafingAbility=0.500000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,80 @@
|
|||
//=============================================================================
|
||||
// ZombieHusk
|
||||
//=============================================================================
|
||||
// Husk burned up fire projectile launching zed pawn class
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHuskBase extends NiceMonster
|
||||
abstract;
|
||||
var float NextFireProjectileTime; // Track when we will fire again
|
||||
var() float ProjectileFireInterval; // How often to fire the fire projectile
|
||||
var() float BurnDamageScale; // How much to reduce fire damage for the Husk
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
ProjectileFireInterval=5.500000
BurnDamageScale=0.250000
bFireImmune=True
bCanBurn=False
fuelRatio=0.000000
clientHeadshotScale=1.100000
MeleeAnims(0)="Strike"
MeleeAnims(1)="Strike"
MeleeAnims(2)="Strike"
BleedOutDuration=6.000000
ZapThreshold=0.750000
bHarpoonToBodyStuns=False
ZombieFlag=1
MeleeDamage=15
damageForce=70000
bFatAss=True
KFRagdollName="Burns_Trip"
Intelligence=BRAINS_Mammal
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=36.000000)
ColRadius=30.000000
ColHeight=33.000000
SeveredArmAttachScale=0.900000
SeveredLegAttachScale=0.900000
SeveredHeadAttachScale=0.900000
PlayerCountHealthScale=0.100000
OnlineHeadshotOffset=(X=20.000000,Z=55.000000)
HeadHealth=200.000000
PlayerNumHeadHealthScale=0.050000
AmmunitionClass=Class'KFMod.BZombieAmmo'
ScoringValue=17
IdleHeavyAnim="Idle"
IdleRifleAnim="Idle"
MeleeRange=30.000000
GroundSpeed=115.000000
WaterSpeed=102.000000
HealthMax=600.000000
Health=600
HeadHeight=1.000000
HeadScale=1.350000
AmbientSoundScaling=8.000000
MenuName="Nice Husk"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkB"
MovementAnims(2)="WalkL"
MovementAnims(3)="WalkR"
WalkAnims(1)="WalkB"
WalkAnims(2)="WalkL"
WalkAnims(3)="WalkR"
IdleCrouchAnim="Idle"
IdleWeaponAnim="Idle"
IdleRestAnim="Idle"
DrawScale=1.400000
PrePivot=(Z=22.000000)
Skins(1)=Shader'KF_Specimens_Trip_T_Two.burns.burns_shdr'
SoundVolume=200
Mass=400.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
//=============================================================================
|
||||
// ZombieHusk
|
||||
//=============================================================================
|
||||
// Husk burned up fire projectile launching zed pawn class
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHuskBase extends NiceMonster
|
||||
abstract;
|
||||
var float NextFireProjectileTime; // Track when we will fire again
|
||||
var() float ProjectileFireInterval; // How often to fire the fire projectile
|
||||
var() float BurnDamageScale; // How much to reduce fire damage for the Husk
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
ProjectileFireInterval=5.500000
|
||||
BurnDamageScale=0.250000
|
||||
bFireImmune=True
|
||||
bCanBurn=False
|
||||
fuelRatio=0.000000
|
||||
clientHeadshotScale=1.100000
|
||||
MeleeAnims(0)="Strike"
|
||||
MeleeAnims(1)="Strike"
|
||||
MeleeAnims(2)="Strike"
|
||||
BleedOutDuration=6.000000
|
||||
ZapThreshold=0.750000
|
||||
bHarpoonToBodyStuns=False
|
||||
ZombieFlag=1
|
||||
MeleeDamage=15
|
||||
damageForce=70000
|
||||
bFatAss=True
|
||||
KFRagdollName="Burns_Trip"
|
||||
Intelligence=BRAINS_Mammal
|
||||
bCanDistanceAttackDoors=True
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=36.000000)
|
||||
ColRadius=30.000000
|
||||
ColHeight=33.000000
|
||||
SeveredArmAttachScale=0.900000
|
||||
SeveredLegAttachScale=0.900000
|
||||
SeveredHeadAttachScale=0.900000
|
||||
PlayerCountHealthScale=0.100000
|
||||
OnlineHeadshotOffset=(X=20.000000,Z=55.000000)
|
||||
HeadHealth=200.000000
|
||||
PlayerNumHeadHealthScale=0.050000
|
||||
AmmunitionClass=Class'KFMod.BZombieAmmo'
|
||||
ScoringValue=17
|
||||
IdleHeavyAnim="Idle"
|
||||
IdleRifleAnim="Idle"
|
||||
MeleeRange=30.000000
|
||||
GroundSpeed=115.000000
|
||||
WaterSpeed=102.000000
|
||||
HealthMax=600.000000
|
||||
Health=600
|
||||
HeadHeight=1.000000
|
||||
HeadScale=1.350000
|
||||
AmbientSoundScaling=8.000000
|
||||
MenuName="Nice Husk"
|
||||
MovementAnims(0)="WalkF"
|
||||
MovementAnims(1)="WalkB"
|
||||
MovementAnims(2)="WalkL"
|
||||
MovementAnims(3)="WalkR"
|
||||
WalkAnims(1)="WalkB"
|
||||
WalkAnims(2)="WalkL"
|
||||
WalkAnims(3)="WalkR"
|
||||
IdleCrouchAnim="Idle"
|
||||
IdleWeaponAnim="Idle"
|
||||
IdleRestAnim="Idle"
|
||||
DrawScale=1.400000
|
||||
PrePivot=(Z=22.000000)
|
||||
Skins(1)=Shader'KF_Specimens_Trip_T_Two.burns.burns_shdr'
|
||||
SoundVolume=200
|
||||
Mass=400.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +1,225 @@
|
|||
//=============================================================================
|
||||
// HuskZombieController
|
||||
//=============================================================================
|
||||
// Controller class for the husk zombie
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHuskController extends NiceMonsterController;
|
||||
// Overridden to create a delay between when the husk fires his projectiles
|
||||
function bool FireWeaponAt(Actor A)
|
||||
{
|
||||
if ( A == none )
A = Enemy;
|
||||
if ( (A == none) || (Focus != A) )
return false;
|
||||
Target = A;
|
||||
if( (VSize(A.Location - Pawn.Location) >= NiceZombieHusk(Pawn).MeleeRange + Pawn.CollisionRadius + Target.CollisionRadius) &&
NiceZombieHusk(Pawn).NextFireProjectileTime - Level.TimeSeconds > 0 )
|
||||
{
return false;
|
||||
}
|
||||
Monster(Pawn).RangedAttack(Target);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
AdjustAim()
|
||||
Returns a rotation which is the direction the bot should aim - after introducing the appropriate aiming error
|
||||
Overridden to cause the zed to fire at the feet more often - Ramm
|
||||
*/
|
||||
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
|
||||
{
|
||||
local rotator FireRotation, TargetLook;
|
||||
local float FireDist, TargetDist, ProjSpeed;
|
||||
local actor HitActor;
|
||||
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
|
||||
local int realYaw;
|
||||
local bool bDefendMelee, bClean, bLeadTargetNow;
|
||||
local bool bWantsToAimAtFeet;
|
||||
if ( FiredAmmunition.ProjectileClass != none )
projspeed = FiredAmmunition.ProjectileClass.default.speed;
|
||||
// make sure bot has a valid target
|
||||
if ( Target == none )
|
||||
{
Target = Enemy;
if ( Target == none )
return Rotation;
|
||||
}
|
||||
FireSpot = Target.Location;
|
||||
TargetDist = VSize(Target.Location - Pawn.Location);
|
||||
// perfect aim at stationary objects
|
||||
if ( Pawn(Target) == none )
|
||||
{
if ( !FiredAmmunition.bTossed )
return rotator(Target.Location - projstart);
else
{
FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);
SetRotation(Rotator(FireDir));
return Rotation;
}
|
||||
}
|
||||
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
|
||||
bDefendMelee = ( (Target == Enemy) && DefendMelee(TargetDist) );
|
||||
aimerror = AdjustAimError(aimerror,TargetDist,bDefendMelee,FiredAmmunition.bInstantHit, bLeadTargetNow);
|
||||
// lead target with non instant hit projectiles
|
||||
if ( bLeadTargetNow )
|
||||
{
TargetVel = Target.Velocity;
// hack guess at projecting falling velocity of target
if ( Target.Physics == PHYS_Falling )
{
if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z )
TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
else
TargetVel.Z = FMin(0, TargetVel.Z);
}
// more or less lead target (with some random variation)
FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
|
||||
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) )
{
// don't always lead far away targets, especially if they are moving sideways with respect to the bot
TargetLook = Target.Rotation;
if ( Target.Physics == PHYS_Walking )
TargetLook.Pitch = 0;
bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
}
else // make sure that bot isn't leading into a wall
bClean = FastTrace(FireSpot, ProjStart);
if ( !bClean)
{
// reduce amount of leading
if ( FRand() < 0.3 )
FireSpot = Target.Location;
else
FireSpot = 0.5 * (FireSpot + Target.Location);
}
|
||||
}
|
||||
bClean = false; //so will fail first check unless shooting at feet
|
||||
// Randomly determine if we should try and splash damage with the fire projectile
|
||||
if( FiredAmmunition.bTrySplash )
|
||||
{
if( Skill < 2.0 )
{
if(FRand() > 0.85)
{
bWantsToAimAtFeet = true;
}
}
else if( Skill < 3.0 )
{
if(FRand() > 0.5)
{
bWantsToAimAtFeet = true;
}
}
else if( Skill >= 3.0 )
{
if(FRand() > 0.25)
{
bWantsToAimAtFeet = true;
}
}
|
||||
}
|
||||
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != none) && (((Target.Physics == PHYS_Falling)
&& (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z)
&& (bDefendMelee || bWantsToAimAtFeet))) )
|
||||
{
HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
|
||||
bClean = (HitActor == none);
if ( !bClean )
{
FireSpot = HitLocation + vect(0,0,3);
bClean = FastTrace(FireSpot, ProjStart);
}
else
bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
|
||||
}
|
||||
if ( !bClean )
|
||||
{
//try middle
FireSpot.Z = Target.Location.Z;
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid )
|
||||
{
FireSpot = LastSeenPos;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
{
bCanFire = false;
FireSpot += 2 * Target.CollisionHeight * HitNormal;
}
bClean = true;
|
||||
}
|
||||
if( !bClean )
|
||||
{
// try head
FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( !bClean && (Target == Enemy) && bEnemyInfoValid )
|
||||
{
FireSpot = LastSeenPos;
if ( Pawn.Location.Z >= LastSeenPos.Z )
FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
{
FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
if ( Monster(Pawn).SplashDamage() && (Skill >= 4) )
{
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
}
bCanFire = false;
}
|
||||
}
|
||||
// adjust for toss distance
|
||||
if ( FiredAmmunition.bTossed )
FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
|
||||
else
FireDir = FireSpot - ProjStart;
|
||||
FireRotation = Rotator(FireDir);
|
||||
realYaw = FireRotation.Yaw;
|
||||
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
|
||||
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
// avoid shooting into wall
|
||||
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
{
if ( HitNormal.Z < 0.7 )
{
FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
FireDir = vector(FireRotation);
FireSpot = ProjStart + FireDist * FireDir;
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
}
if ( HitActor != none )
{
FireSpot += HitNormal * 2 * Target.CollisionHeight;
if ( Skill >= 4 )
{
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
if ( HitActor != none )
FireSpot += Target.CollisionHeight * HitNormal;
}
FireDir = Normal(FireSpot - ProjStart);
FireRotation = rotator(FireDir);
}
|
||||
}
|
||||
SetRotation(FireRotation);
|
||||
return FireRotation;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
//=============================================================================
|
||||
// HuskZombieController
|
||||
//=============================================================================
|
||||
// Controller class for the husk zombie
|
||||
//=============================================================================
|
||||
// Killing Floor Source
|
||||
// Copyright (C) 2009 Tripwire Interactive LLC
|
||||
// - John "Ramm-Jaeger" Gibson
|
||||
//=============================================================================
|
||||
class NiceZombieHuskController extends NiceMonsterController;
|
||||
// Overridden to create a delay between when the husk fires his projectiles
|
||||
function bool FireWeaponAt(Actor A)
|
||||
{
|
||||
if ( A == none )
|
||||
A = Enemy;
|
||||
if ( (A == none) || (Focus != A) )
|
||||
return false;
|
||||
Target = A;
|
||||
if( (VSize(A.Location - Pawn.Location) >= NiceZombieHusk(Pawn).MeleeRange + Pawn.CollisionRadius + Target.CollisionRadius) &&
|
||||
NiceZombieHusk(Pawn).NextFireProjectileTime - Level.TimeSeconds > 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Monster(Pawn).RangedAttack(Target);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
AdjustAim()
|
||||
Returns a rotation which is the direction the bot should aim - after introducing the appropriate aiming error
|
||||
Overridden to cause the zed to fire at the feet more often - Ramm
|
||||
*/
|
||||
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
|
||||
{
|
||||
local rotator FireRotation, TargetLook;
|
||||
local float FireDist, TargetDist, ProjSpeed;
|
||||
local actor HitActor;
|
||||
local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
|
||||
local int realYaw;
|
||||
local bool bDefendMelee, bClean, bLeadTargetNow;
|
||||
local bool bWantsToAimAtFeet;
|
||||
if ( FiredAmmunition.ProjectileClass != none )
|
||||
projspeed = FiredAmmunition.ProjectileClass.default.speed;
|
||||
// make sure bot has a valid target
|
||||
if ( Target == none )
|
||||
{
|
||||
Target = Enemy;
|
||||
if ( Target == none )
|
||||
return Rotation;
|
||||
}
|
||||
FireSpot = Target.Location;
|
||||
TargetDist = VSize(Target.Location - Pawn.Location);
|
||||
// perfect aim at stationary objects
|
||||
if ( Pawn(Target) == none )
|
||||
{
|
||||
if ( !FiredAmmunition.bTossed )
|
||||
return rotator(Target.Location - projstart);
|
||||
else
|
||||
{
|
||||
FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);
|
||||
SetRotation(Rotator(FireDir));
|
||||
return Rotation;
|
||||
}
|
||||
}
|
||||
bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
|
||||
bDefendMelee = ( (Target == Enemy) && DefendMelee(TargetDist) );
|
||||
aimerror = AdjustAimError(aimerror,TargetDist,bDefendMelee,FiredAmmunition.bInstantHit, bLeadTargetNow);
|
||||
// lead target with non instant hit projectiles
|
||||
if ( bLeadTargetNow )
|
||||
{
|
||||
TargetVel = Target.Velocity;
|
||||
// hack guess at projecting falling velocity of target
|
||||
if ( Target.Physics == PHYS_Falling )
|
||||
{
|
||||
if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z )
|
||||
TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
|
||||
else
|
||||
TargetVel.Z = FMin(0, TargetVel.Z);
|
||||
}
|
||||
// more or less lead target (with some random variation)
|
||||
FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
|
||||
FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
|
||||
|
||||
if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) )
|
||||
{
|
||||
// don't always lead far away targets, especially if they are moving sideways with respect to the bot
|
||||
TargetLook = Target.Rotation;
|
||||
if ( Target.Physics == PHYS_Walking )
|
||||
TargetLook.Pitch = 0;
|
||||
bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
|
||||
}
|
||||
else // make sure that bot isn't leading into a wall
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
if ( !bClean)
|
||||
{
|
||||
// reduce amount of leading
|
||||
if ( FRand() < 0.3 )
|
||||
FireSpot = Target.Location;
|
||||
else
|
||||
FireSpot = 0.5 * (FireSpot + Target.Location);
|
||||
}
|
||||
}
|
||||
bClean = false; //so will fail first check unless shooting at feet
|
||||
// Randomly determine if we should try and splash damage with the fire projectile
|
||||
if( FiredAmmunition.bTrySplash )
|
||||
{
|
||||
if( Skill < 2.0 )
|
||||
{
|
||||
if(FRand() > 0.85)
|
||||
{
|
||||
bWantsToAimAtFeet = true;
|
||||
}
|
||||
}
|
||||
else if( Skill < 3.0 )
|
||||
{
|
||||
if(FRand() > 0.5)
|
||||
{
|
||||
bWantsToAimAtFeet = true;
|
||||
}
|
||||
}
|
||||
else if( Skill >= 3.0 )
|
||||
{
|
||||
if(FRand() > 0.25)
|
||||
{
|
||||
bWantsToAimAtFeet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( FiredAmmunition.bTrySplash && (Pawn(Target) != none) && (((Target.Physics == PHYS_Falling)
|
||||
&& (Pawn.Location.Z + 80 >= Target.Location.Z)) || ((Pawn.Location.Z + 19 >= Target.Location.Z)
|
||||
&& (bDefendMelee || bWantsToAimAtFeet))) )
|
||||
{
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 10), FireSpot, false);
|
||||
|
||||
bClean = (HitActor == none);
|
||||
if ( !bClean )
|
||||
{
|
||||
FireSpot = HitLocation + vect(0,0,3);
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
else
|
||||
bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
|
||||
}
|
||||
if ( !bClean )
|
||||
{
|
||||
//try middle
|
||||
FireSpot.Z = Target.Location.Z;
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid )
|
||||
{
|
||||
FireSpot = LastSeenPos;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
{
|
||||
bCanFire = false;
|
||||
FireSpot += 2 * Target.CollisionHeight * HitNormal;
|
||||
}
|
||||
bClean = true;
|
||||
}
|
||||
if( !bClean )
|
||||
{
|
||||
// try head
|
||||
FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
|
||||
bClean = FastTrace(FireSpot, ProjStart);
|
||||
}
|
||||
if ( !bClean && (Target == Enemy) && bEnemyInfoValid )
|
||||
{
|
||||
FireSpot = LastSeenPos;
|
||||
if ( Pawn.Location.Z >= LastSeenPos.Z )
|
||||
FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
{
|
||||
FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
|
||||
if ( Monster(Pawn).SplashDamage() && (Skill >= 4) )
|
||||
{
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
|
||||
}
|
||||
bCanFire = false;
|
||||
}
|
||||
}
|
||||
// adjust for toss distance
|
||||
if ( FiredAmmunition.bTossed )
|
||||
FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
|
||||
else
|
||||
FireDir = FireSpot - ProjStart;
|
||||
FireRotation = Rotator(FireDir);
|
||||
realYaw = FireRotation.Yaw;
|
||||
InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
|
||||
FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
// avoid shooting into wall
|
||||
FireDist = FMin(VSize(FireSpot-ProjStart), 400);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
{
|
||||
if ( HitNormal.Z < 0.7 )
|
||||
{
|
||||
FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
|
||||
FireDir = vector(FireRotation);
|
||||
FireSpot = ProjStart + FireDist * FireDir;
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
}
|
||||
if ( HitActor != none )
|
||||
{
|
||||
FireSpot += HitNormal * 2 * Target.CollisionHeight;
|
||||
if ( Skill >= 4 )
|
||||
{
|
||||
HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
|
||||
if ( HitActor != none )
|
||||
FireSpot += Target.CollisionHeight * HitNormal;
|
||||
}
|
||||
FireDir = Normal(FireSpot - ProjStart);
|
||||
FireRotation = rotator(FireDir);
|
||||
}
|
||||
}
|
||||
SetRotation(FireRotation);
|
||||
return FireRotation;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +1,180 @@
|
|||
class NiceZombieJason extends NiceZombieScrake;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
var int OriginalMeleeDamage; // default melee damage, adjusted by game's difficulty
|
||||
var bool bWasRaged; // set to true, if Jason is raged or was raged before
|
||||
var float RageHealthPct;
|
||||
var float RegenDelay;
|
||||
var float RegenRate; // Speed of regeneration, in percents of max health
|
||||
var float RegenAcc;
|
||||
var float RegenAccHead;
|
||||
simulated function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
OriginalMeleeDamage = MeleeDamage;
|
||||
}
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
return super.CheckMiniFlinch(flinchScore * 1.5, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
super.Tick(DeltaTime);
|
||||
if(Role < ROLE_Authority)
return;
|
||||
if(lastTookDamageTime + RegenDelay < Level.TimeSeconds && Health > 0){
RegenAcc += DeltaTime * RegenRate * HealthMax * 0.01;
RegenAccHead += DeltaTime * RegenRate * HeadHealthMax * 0.01;
if(RegenAcc > 1){
Health += RegenAcc;
if(Health > HealthMax)
Health = HealthMax;
RegenAcc = 0.0;
}
if(RegenAccHead > 1){
HeadHealth += RegenAccHead;
if(HeadHealth > HeadHealthMax)
HeadHealth = HeadHealthMax;
RegenAccHead = 0.0;
}
|
||||
}
|
||||
else{
RegenAcc = 0.0;
RegenAccHead = 0.0;
|
||||
}
|
||||
}
|
||||
// Machete has no Exhaust ;)
|
||||
simulated function SpawnExhaustEmitter(){}
|
||||
simulated function UpdateExhaustEmitter(){}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return !bIsStunned; // can't dodge husk fireballs while stunned
|
||||
}
|
||||
simulated function Unstun(){
|
||||
bCharging = true;
|
||||
MovementAnims[0] = 'ChargeF';
|
||||
super.Unstun();
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if(bIsStunned && Health > 0 && (headshotLevel <= 0.0) && Level.TimeSeconds > LastStunTime + 0.1)
Unstun();
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator){
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bIsStunned && Health > 0 && Damage > 150 && Level.TimeSeconds > LastStunTime + 0.1)
Unstun();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
if(NiceMonster(A) == none)
GoToState('SawingLoop');
|
||||
}
|
||||
if( !bShotAnim && !bDecapitated ) {
if(bConfusedState)
return;
if ( bWasRaged || float(Health)/HealthMax < 0.5
|| (float(Health)/HealthMax < RageHealthPct) )
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
State SawingLoop
|
||||
{
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim )
return;
else if ( CanAttack(A) )
{
Acceleration = vect(0,0,0);
bShotAnim = true;
MeleeDamage = OriginalMeleeDamage * 0.6;
SetAnimAction('SawImpaleLoop');
if( AmbientSound != SawAttackLoopSound )
{
AmbientSound=SawAttackLoopSound;
}
}
else GoToState('');
|
||||
}
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed()
|
||||
{
|
||||
local float result;
|
||||
result = OriginalGroundSpeed;
|
||||
if ( bWasRaged || bCharging )
result *= 3.5;
|
||||
else if( bZedUnderControl )
result *= 1.25;
return result;
|
||||
}
|
||||
state RunningState
|
||||
{
|
||||
function BeginState()
|
||||
{
local NiceHumanPawn rageTarget;
bWasRaged = true;
if(bWasCalm){
bWasCalm = false;
rageTarget = NiceHumanPawn(Controller.focus);
if( rageTarget != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
}
if( bZapped )
GoToState('');
else {
bCharging = true;
SetGroundSpeed(GetOriginalGroundSpeed());
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function EndState()
|
||||
{
bCharging = False;
if( !bZapped )
SetGroundSpeed(GetOriginalGroundSpeed());
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
RageHealthPct=0.750000
RegenDelay=5.000000
RegenRate=4.000000
SawAttackLoopSound=Sound'KF_BaseGorefast.Attack.Gorefast_AttackSwish3'
ChainSawOffSound=None
StunThreshold=1.000000
MoanVoice=None
StunsRemaining=5
BleedOutDuration=7.000000
MeleeDamage=25
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=None
HeadHealth=800.000000
HitSound(0)=None
DeathSound(0)=None
ChallengeSound(0)=None
ChallengeSound(1)=None
ChallengeSound(2)=None
ChallengeSound(3)=None
ScoringValue=300
MenuName="Jason"
AmbientSound=Sound'ScrnZedPack_S.Jason.Jason_Sound'
Mesh=SkeletalMesh'ScrnZedPack_A.JasonMesh'
Skins(0)=Shader'ScrnZedPack_T.Jason.Jason__FB'
Skins(1)=Texture'ScrnZedPack_T.Jason.JVMaskB'
Skins(2)=Combiner'ScrnZedPack_T.Jason.Machete_cmb'
|
||||
}
|
||||
class NiceZombieJason extends NiceZombieScrake;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
var int OriginalMeleeDamage; // default melee damage, adjusted by game's difficulty
|
||||
var bool bWasRaged; // set to true, if Jason is raged or was raged before
|
||||
var float RageHealthPct;
|
||||
var float RegenDelay;
|
||||
var float RegenRate; // Speed of regeneration, in percents of max health
|
||||
var float RegenAcc;
|
||||
var float RegenAccHead;
|
||||
simulated function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
OriginalMeleeDamage = MeleeDamage;
|
||||
}
|
||||
function bool IsStunPossible(){
|
||||
return false;
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
return super.CheckMiniFlinch(flinchScore * 1.5, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function Tick(float DeltaTime){
|
||||
super.Tick(DeltaTime);
|
||||
if(Role < ROLE_Authority)
|
||||
return;
|
||||
if(lastTookDamageTime + RegenDelay < Level.TimeSeconds && Health > 0){
|
||||
RegenAcc += DeltaTime * RegenRate * HealthMax * 0.01;
|
||||
RegenAccHead += DeltaTime * RegenRate * HeadHealthMax * 0.01;
|
||||
if(RegenAcc > 1){
|
||||
Health += RegenAcc;
|
||||
if(Health > HealthMax)
|
||||
Health = HealthMax;
|
||||
RegenAcc = 0.0;
|
||||
}
|
||||
if(RegenAccHead > 1){
|
||||
HeadHealth += RegenAccHead;
|
||||
if(HeadHealth > HeadHealthMax)
|
||||
HeadHealth = HeadHealthMax;
|
||||
RegenAccHead = 0.0;
|
||||
}
|
||||
}
|
||||
else{
|
||||
RegenAcc = 0.0;
|
||||
RegenAccHead = 0.0;
|
||||
}
|
||||
}
|
||||
// Machete has no Exhaust ;)
|
||||
simulated function SpawnExhaustEmitter(){}
|
||||
simulated function UpdateExhaustEmitter(){}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return !bIsStunned; // can't dodge husk fireballs while stunned
|
||||
}
|
||||
simulated function Unstun(){
|
||||
bCharging = true;
|
||||
MovementAnims[0] = 'ChargeF';
|
||||
super.Unstun();
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
Super.TakeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if(bIsStunned && Health > 0 && (headshotLevel <= 0.0) && Level.TimeSeconds > LastStunTime + 0.1)
|
||||
Unstun();
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator){
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bIsStunned && Health > 0 && Damage > 150 && Level.TimeSeconds > LastStunTime + 0.1)
|
||||
Unstun();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
|
||||
bShotAnim = true;
|
||||
SetAnimAction(MeleeAnims[Rand(2)]);
|
||||
if(NiceMonster(A) == none)
|
||||
GoToState('SawingLoop');
|
||||
}
|
||||
if( !bShotAnim && !bDecapitated ) {
|
||||
if(bConfusedState)
|
||||
return;
|
||||
if ( bWasRaged || float(Health)/HealthMax < 0.5
|
||||
|| (float(Health)/HealthMax < RageHealthPct) )
|
||||
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
State SawingLoop
|
||||
{
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
|
||||
Acceleration = vect(0,0,0);
|
||||
bShotAnim = true;
|
||||
MeleeDamage = OriginalMeleeDamage * 0.6;
|
||||
SetAnimAction('SawImpaleLoop');
|
||||
if( AmbientSound != SawAttackLoopSound )
|
||||
{
|
||||
AmbientSound=SawAttackLoopSound;
|
||||
}
|
||||
}
|
||||
else GoToState('');
|
||||
}
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed()
|
||||
{
|
||||
local float result;
|
||||
result = OriginalGroundSpeed;
|
||||
if ( bWasRaged || bCharging )
|
||||
result *= 3.5;
|
||||
else if( bZedUnderControl )
|
||||
result *= 1.25;
|
||||
return result;
|
||||
}
|
||||
state RunningState
|
||||
{
|
||||
function BeginState()
|
||||
{
|
||||
local NiceHumanPawn rageTarget;
|
||||
bWasRaged = true;
|
||||
if(bWasCalm){
|
||||
bWasCalm = false;
|
||||
rageTarget = NiceHumanPawn(Controller.focus);
|
||||
if( rageTarget != none && KFGameType(Level.Game) != none
|
||||
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
|
||||
class'NiceSkillCommandoPerfectExecution') ){
|
||||
KFGameType(Level.Game).DramaticEvent(1.0);
|
||||
}
|
||||
}
|
||||
if( bZapped )
|
||||
GoToState('');
|
||||
else {
|
||||
bCharging = true;
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
bCharging = False;
|
||||
if( !bZapped )
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
PostNetReceive();
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
RageHealthPct=0.750000
|
||||
RegenDelay=5.000000
|
||||
RegenRate=4.000000
|
||||
SawAttackLoopSound=Sound'KF_BaseGorefast.Attack.Gorefast_AttackSwish3'
|
||||
ChainSawOffSound=None
|
||||
StunThreshold=1.000000
|
||||
MoanVoice=None
|
||||
StunsRemaining=5
|
||||
BleedOutDuration=7.000000
|
||||
MeleeDamage=25
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
|
||||
JumpSound=None
|
||||
HeadHealth=800.000000
|
||||
HitSound(0)=None
|
||||
DeathSound(0)=None
|
||||
ChallengeSound(0)=None
|
||||
ChallengeSound(1)=None
|
||||
ChallengeSound(2)=None
|
||||
ChallengeSound(3)=None
|
||||
ScoringValue=300
|
||||
MenuName="Jason"
|
||||
AmbientSound=Sound'ScrnZedPack_S.Jason.Jason_Sound'
|
||||
Mesh=SkeletalMesh'ScrnZedPack_A.JasonMesh'
|
||||
Skins(0)=Shader'ScrnZedPack_T.Jason.Jason__FB'
|
||||
Skins(1)=Texture'ScrnZedPack_T.Jason.JVMaskB'
|
||||
Skins(2)=Combiner'ScrnZedPack_T.Jason.Machete_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,297 +1,657 @@
|
|||
// Chainsaw Zombie Monster for KF Invasion gametype
|
||||
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
|
||||
class NiceZombieScrake extends NiceZombieScrakeBase;
|
||||
var bool bConfusedState;
|
||||
var bool bWasCalm;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
EnableChannelNotify ( 1,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
super.PostNetBeginPlay();
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
bWasCalm = true;
|
||||
SpawnExhaustEmitter();
|
||||
}
|
||||
// Make the scrakes's ambient scale higher, since there are just a few, and thier chainsaw need to be heard from a distance
|
||||
simulated function CalcAmbientRelevancyScale()
|
||||
{
|
||||
// Make the zed only relevant by their ambient sound out to a range of 30 meters
|
||||
CustomAmbientRelevancyScale = 1500 / (100 * SoundRadius);
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
if (bCharging)
MovementAnims[0]='ChargeF';
|
||||
else if( !(bCrispified && bBurnified) )
MovementAnims[0]=default.MovementAnims[0];
|
||||
}
|
||||
// Deprecated
|
||||
function bool FlipOverWithIntsigator(Pawn InstigatedBy){
|
||||
local bool bFlippedOver;
|
||||
bFlippedOver = super.FlipOverWithIntsigator(InstigatedBy);
|
||||
if(bFlippedOver){
// do not rotate while stunned
Controller.Focus = none;
Controller.FocalPoint = Location + 512*vector(Rotation);
|
||||
}
|
||||
return bFlippedOver;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function float GetIceCrustScale(){
|
||||
//return 25000 / (default.health * default.health);
|
||||
return 0.01;
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
NumZCDHits++;
|
||||
// if we hit him a couple of times, make him rage!
if( NumZCDHits > 1 )
{
if( !IsInState('RunningToMarker') )
{
GotoState('RunningToMarker');
}
else
{
NumZCDHits = 1;
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
}
else
{
if( IsInState('RunningToMarker') )
{
GotoState('');
}
}
|
||||
if( bNewMindControlled != bZedUnderControl )
{
SetGroundSpeed(OriginalGroundSpeed * 1.25);
Health *= 1.25;
HealthMax *= 1.25;
}
|
||||
}
|
||||
else
|
||||
{
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bCharging && NumZCDHits > 1 )
|
||||
{
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
GotoState('');
|
||||
}
|
||||
}
|
||||
simulated function SpawnExhaustEmitter()
|
||||
{
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
if ( ExhaustEffectClass != none )
{
ExhaustEffect = Spawn(ExhaustEffectClass, self);
|
||||
if ( ExhaustEffect != none )
{
AttachToBone(ExhaustEffect, 'Chainsaw_lod1');
ExhaustEffect.SetRelativeLocation(vect(0, -20, 0));
}
}
|
||||
}
|
||||
}
|
||||
simulated function UpdateExhaustEmitter()
|
||||
{
|
||||
local byte Throttle;
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
if ( ExhaustEffect != none )
{
if ( bShotAnim )
{
Throttle = 3;
}
else
{
Throttle = 0;
}
}
else
{
if ( !bNoExhaustRespawn )
{
SpawnExhaustEmitter();
}
}
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
UpdateExhaustEmitter();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
if(NiceMonster(A) == none)
GoToState('SawingLoop');
|
||||
}
|
||||
if( !bShotAnim && !bDecapitated )
|
||||
{
if(bConfusedState)
return;
if ( float(Health)/HealthMax < 0.75)
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
state RunningState
|
||||
{
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
Global.SetZappedBehavior();
GoToState('');
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed() {
return 3.5 * OriginalGroundSpeed;
|
||||
}
|
||||
function BeginState(){
local NiceHumanPawn rageTarget, rageCause;
|
||||
if(Health <= 0)
return;
|
||||
if(bWasCalm){
bWasCalm = false;
rageTarget = NiceHumanPawn(Controller.focus);
rageCause = NiceHumanPawn(LastDamagedBy);
if( rageTarget != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
else if( rageCause != none && KFGameType(Level.Game) != none
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageCause.Controller),
class'NiceSkillCommandoPerfectExecution') ){
KFGameType(Level.Game).DramaticEvent(1.0);
}
}
if(bZapped)
GoToState('');
else{
SetGroundSpeed(OriginalGroundSpeed * 3.5);
bCharging = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function EndState()
|
||||
{
if( !bZapped )
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
bCharging = False;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
}
|
||||
function RemoveHead()
|
||||
{
GoToState('');
Global.RemoveHead();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim || Physics == PHYS_Swimming)
return;
else if ( CanAttack(A) )
{
bShotAnim = true;
SetAnimAction(MeleeAnims[Rand(2)]);
if(NiceMonster(A) == none)
GoToState('SawingLoop');
}
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
// Not sure if we need this since its just like RageCharging,
|
||||
// but keeping it here for now in case we need to implement some
|
||||
// custom behavior for this state
|
||||
state RunningToMarker extends RunningState
|
||||
{
|
||||
}
|
||||
|
||||
State SawingLoop
|
||||
{
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed() {
return OriginalGroundSpeed * AttackChargeRate;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
bConfusedState = false;
|
||||
// Randomly have the scrake charge during an attack so it will be less predictable
if(Health/HealthMax < 0.5 || FRand() <= 0.95)
{
SetGroundSpeed(OriginalGroundSpeed * AttackChargeRate);
bCharging = true;
if( Level.NetMode!=NM_DedicatedServer )
PostNetReceive();
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
if ( bShotAnim )
return;
else if ( CanAttack(A) )
{
Acceleration = vect(0,0,0);
bShotAnim = true;
MeleeDamage = default.MeleeDamage*0.6;
SetAnimAction('SawImpaleLoop');
if( AmbientSound != SawAttackLoopSound )
{
AmbientSound=SawAttackLoopSound;
}
}
else GoToState('');
|
||||
}
|
||||
function AnimEnd( int Channel )
|
||||
{
Super.AnimEnd(Channel);
if( Controller!=none && Controller.Enemy!=none )
RangedAttack(Controller.Enemy); // Keep on attacking if possible.
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
// Keep the scrake moving toward its target when attacking
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim )
{
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function EndState()
|
||||
{
AmbientSound=default.AmbientSound;
MeleeDamage = Max( DifficultyDamageModifer() * default.MeleeDamage, 1 );
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
bCharging = False;
if(Level.NetMode != NM_DedicatedServer)
PostNetReceive();
|
||||
}
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
super.ModDamage(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
if(damageType == class'ScrnZedPack.DamTypeEMP')
Damage *= 0.01;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local bool bCanGetConfused;
|
||||
local int OldHealth;
|
||||
local PlayerController PC;
|
||||
local KFSteamStatsAndAchievements Stats;
|
||||
OldHealth = Health;
|
||||
bCanGetConfused = false;
|
||||
if(StunsRemaining != 0 && float(Health)/HealthMax >= 0.75)
bCanGetConfused = true;
|
||||
super.takeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if (
bCanGetConfused &&
!IsInState('SawingLoop') &&
(OldHealth - Health) <= (float(default.Health)/1.5) && float(Health)/HealthMax < 0.75 &&
(LastDamageAmount >= (0.5 * default.Health) ||
(VSize(LastDamagedBy.Location - Location) <= (MeleeRange * 2) && ClassIsChildOf(LastDamagedbyType,class 'DamTypeMelee') &&
KFPawn(LastDamagedBy) != none && LastDamageAmount > (0.10 * default.Health)))
)
bConfusedState = true;
|
||||
if(bConfusedState && Health > 0 && (headshotLevel <= 0.0) && damageType != none){
bConfusedState = false;
GoToState('RunningState');
|
||||
}
|
||||
if(!bConfusedState && !IsInState('SawingLoop') && !IsInState('RunningState') && float(Health) / HealthMax < 0.75)
RangedAttack(InstigatedBy);
|
||||
if(damageType == class'DamTypeDBShotgun'){
PC = PlayerController( InstigatedBy.Controller );
if(PC != none){
Stats = KFSteamStatsAndAchievements( PC.SteamStatsAndAchievements );
if( Stats != none )
Stats.CheckAndSetAchievementComplete( Stats.KFACHIEVEMENT_PushScrakeSPJ );
}
|
||||
}
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator)
|
||||
{
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bConfusedState && Health > 0 && Damage > 150){
bConfusedState = false;
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
// Scrakes are better at enduring pain, so we need a bit more to flinch them
|
||||
if(StunsRemaining == 0 || flinchScore < 150)
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
|
||||
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
StunsRemaining = 0;
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='SawZombieAttack1' || AnimName=='SawZombieAttack2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
Return 1;
|
||||
}
|
||||
Return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'SawImpaleLoop' || TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
|
||||
{
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if ( bGibbed )
{
// Do nothing for now
PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.0,true,525);
return;
}
|
||||
if( bDecapitated )
{
|
||||
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(DeathSound[0], SLOT_Pain,1.30,true,525);
}
|
||||
PlaySound(ChainSawOffSound, SLOT_Misc, 2.0,,525.0);
|
||||
}
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
AmbientSound = none;
|
||||
if ( ExhaustEffect != none )
|
||||
{
ExhaustEffect.Destroy();
ExhaustEffect = none;
bNoExhaustRespawn = true;
|
||||
}
|
||||
super.Died( Killer, damageType, HitLocation );
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
HitFX[SimHitFxTicker].bSever = false;
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
if( !bLeftArmGibbed )
{
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
bLeftArmGibbed=true;
}
break;
|
||||
case RightFArmBone:
if( !bRightArmGibbed )
{
SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightArmGibbed=true;
}
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
// Maybe spawn some chunks when the player gets obliterated
|
||||
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation)
|
||||
{
|
||||
if ( ExhaustEffect != none )
|
||||
{
ExhaustEffect.Destroy();
ExhaustEffect = none;
bNoExhaustRespawn = true;
|
||||
}
|
||||
super.SpawnGibs(HitRotation,ChunkPerterbation);
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.scrake_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_saw_panner');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_FB');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.Chainsaw_blade_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
SawAttackLoopSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Impale'
ChainSawOffSound=SoundGroup'KF_ChainsawSnd.Chainsaw_Deselect'
remainingStuns=1
stunLoopStart=0.240000
stunLoopEnd=0.820000
idleInsertFrame=0.900000
EventClasses(0)="NicePack.NiceZombieScrake"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Chainsaw_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Jump'
DetachedArmClass=Class'KFChar.SeveredArmScrake'
DetachedLegClass=Class'KFChar.SeveredLegScrake'
DetachedHeadClass=Class'KFChar.SeveredHeadScrake'
DetachedSpecialArmClass=Class'KFChar.SeveredArmScrakeSaw'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
ControllerClass=Class'NicePack.NiceZombieScrakeController'
AmbientSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Idle'
Mesh=SkeletalMesh'KF_Freaks_Trip.Scrake_Freak'
Skins(0)=Shader'KF_Specimens_Trip_T.scrake_FB'
Skins(1)=TexPanner'KF_Specimens_Trip_T.scrake_saw_panner'
|
||||
}
|
||||
// Chainsaw Zombie Monster for KF Invasion gametype
|
||||
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
|
||||
class NiceZombieScrake extends NiceZombieScrakeBase;
|
||||
var bool bConfusedState;
|
||||
var bool bWasCalm;
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
EnableChannelNotify ( 1,1);
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
super.PostNetBeginPlay();
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
bWasCalm = true;
|
||||
SpawnExhaustEmitter();
|
||||
}
|
||||
// Make the scrakes's ambient scale higher, since there are just a few, and thier chainsaw need to be heard from a distance
|
||||
simulated function CalcAmbientRelevancyScale()
|
||||
{
|
||||
// Make the zed only relevant by their ambient sound out to a range of 30 meters
|
||||
CustomAmbientRelevancyScale = 1500 / (100 * SoundRadius);
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
if (bCharging)
|
||||
MovementAnims[0]='ChargeF';
|
||||
else if( !(bCrispified && bBurnified) )
|
||||
MovementAnims[0]=default.MovementAnims[0];
|
||||
}
|
||||
// Deprecated
|
||||
function bool FlipOverWithIntsigator(Pawn InstigatedBy){
|
||||
local bool bFlippedOver;
|
||||
bFlippedOver = super.FlipOverWithIntsigator(InstigatedBy);
|
||||
if(bFlippedOver){
|
||||
// do not rotate while stunned
|
||||
Controller.Focus = none;
|
||||
Controller.FocalPoint = Location + 512*vector(Rotation);
|
||||
}
|
||||
return bFlippedOver;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function float GetIceCrustScale(){
|
||||
//return 25000 / (default.health * default.health);
|
||||
return 0.01;
|
||||
}
|
||||
// This zed has been taken control of. Boost its health and speed
|
||||
function SetMindControlled(bool bNewMindControlled)
|
||||
{
|
||||
if( bNewMindControlled )
|
||||
{
|
||||
NumZCDHits++;
|
||||
|
||||
// if we hit him a couple of times, make him rage!
|
||||
if( NumZCDHits > 1 )
|
||||
{
|
||||
if( !IsInState('RunningToMarker') )
|
||||
{
|
||||
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
|
||||
NumZCDHits = 1;
|
||||
if( IsInState('RunningToMarker') )
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsInState('RunningToMarker') )
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
|
||||
if( bNewMindControlled != bZedUnderControl )
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 1.25);
|
||||
Health *= 1.25;
|
||||
HealthMax *= 1.25;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NumZCDHits=0;
|
||||
}
|
||||
bZedUnderControl = bNewMindControlled;
|
||||
}
|
||||
// Handle the zed being commanded to move to a new location
|
||||
function GivenNewMarker()
|
||||
{
|
||||
if( bCharging && NumZCDHits > 1 )
|
||||
{
|
||||
GotoState('RunningToMarker');
|
||||
}
|
||||
else
|
||||
{
|
||||
GotoState('');
|
||||
}
|
||||
}
|
||||
simulated function SpawnExhaustEmitter()
|
||||
{
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
|
||||
if ( ExhaustEffectClass != none )
|
||||
{
|
||||
ExhaustEffect = Spawn(ExhaustEffectClass, self);
|
||||
|
||||
if ( ExhaustEffect != none )
|
||||
{
|
||||
AttachToBone(ExhaustEffect, 'Chainsaw_lod1');
|
||||
ExhaustEffect.SetRelativeLocation(vect(0, -20, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function UpdateExhaustEmitter()
|
||||
{
|
||||
local byte Throttle;
|
||||
if ( Level.NetMode != NM_DedicatedServer )
|
||||
{
|
||||
if ( ExhaustEffect != none )
|
||||
{
|
||||
if ( bShotAnim )
|
||||
{
|
||||
Throttle = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
Throttle = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !bNoExhaustRespawn )
|
||||
{
|
||||
SpawnExhaustEmitter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
super.Tick(DeltaTime);
|
||||
UpdateExhaustEmitter();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
|
||||
bShotAnim = true;
|
||||
SetAnimAction(MeleeAnims[Rand(2)]);
|
||||
//PlaySound(sound'Claw2s', SLOT_none); KFTODO: Replace this
|
||||
if(NiceMonster(A) == none)
|
||||
GoToState('SawingLoop');
|
||||
}
|
||||
if( !bShotAnim && !bDecapitated )
|
||||
{
|
||||
if(bConfusedState)
|
||||
return;
|
||||
if ( float(Health)/HealthMax < 0.75)
|
||||
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
state RunningState
|
||||
{
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
Global.SetZappedBehavior();
|
||||
GoToState('');
|
||||
}
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed() {
|
||||
return 3.5 * OriginalGroundSpeed;
|
||||
}
|
||||
function BeginState(){
|
||||
local NiceHumanPawn rageTarget, rageCause;
|
||||
|
||||
if(Health <= 0)
|
||||
return;
|
||||
|
||||
if(bWasCalm){
|
||||
bWasCalm = false;
|
||||
rageTarget = NiceHumanPawn(Controller.focus);
|
||||
rageCause = NiceHumanPawn(LastDamagedBy);
|
||||
if( rageTarget != none && KFGameType(Level.Game) != none
|
||||
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageTarget.Controller),
|
||||
class'NiceSkillCommandoPerfectExecution') ){
|
||||
KFGameType(Level.Game).DramaticEvent(1.0);
|
||||
}
|
||||
else if( rageCause != none && KFGameType(Level.Game) != none
|
||||
&& class'NiceVeterancyTypes'.static.HasSkill(NicePlayerController(rageCause.Controller),
|
||||
class'NiceSkillCommandoPerfectExecution') ){
|
||||
KFGameType(Level.Game).DramaticEvent(1.0);
|
||||
}
|
||||
}
|
||||
if(bZapped)
|
||||
GoToState('');
|
||||
else{
|
||||
SetGroundSpeed(OriginalGroundSpeed * 3.5);
|
||||
bCharging = true;
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
PostNetReceive();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
if( !bZapped )
|
||||
{
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
bCharging = False;
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
PostNetReceive();
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
GoToState('');
|
||||
Global.RemoveHead();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
|
||||
bShotAnim = true;
|
||||
SetAnimAction(MeleeAnims[Rand(2)]);
|
||||
if(NiceMonster(A) == none)
|
||||
GoToState('SawingLoop');
|
||||
}
|
||||
}
|
||||
}
|
||||
// State where the zed is charging to a marked location.
|
||||
// Not sure if we need this since its just like RageCharging,
|
||||
// but keeping it here for now in case we need to implement some
|
||||
// custom behavior for this state
|
||||
state RunningToMarker extends RunningState
|
||||
{
|
||||
}
|
||||
|
||||
State SawingLoop
|
||||
{
|
||||
// Don't override speed in this state
|
||||
function bool CanSpeedAdjust()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed() {
|
||||
return OriginalGroundSpeed * AttackChargeRate;
|
||||
}
|
||||
function bool CanGetOutOfWay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
bConfusedState = false;
|
||||
|
||||
// Randomly have the scrake charge during an attack so it will be less predictable
|
||||
if(Health/HealthMax < 0.5 || FRand() <= 0.95)
|
||||
{
|
||||
SetGroundSpeed(OriginalGroundSpeed * AttackChargeRate);
|
||||
bCharging = true;
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
PostNetReceive();
|
||||
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
else if ( CanAttack(A) )
|
||||
{
|
||||
Acceleration = vect(0,0,0);
|
||||
bShotAnim = true;
|
||||
MeleeDamage = default.MeleeDamage*0.6;
|
||||
SetAnimAction('SawImpaleLoop');
|
||||
if( AmbientSound != SawAttackLoopSound )
|
||||
{
|
||||
AmbientSound=SawAttackLoopSound;
|
||||
}
|
||||
}
|
||||
else GoToState('');
|
||||
}
|
||||
function AnimEnd( int Channel )
|
||||
{
|
||||
Super.AnimEnd(Channel);
|
||||
if( Controller!=none && Controller.Enemy!=none )
|
||||
RangedAttack(Controller.Enemy); // Keep on attacking if possible.
|
||||
}
|
||||
function Tick( float Delta )
|
||||
{
|
||||
// Keep the scrake moving toward its target when attacking
|
||||
if( Role == ROLE_Authority && bShotAnim && !bWaitForAnim )
|
||||
{
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
|
||||
global.Tick(Delta);
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
AmbientSound=default.AmbientSound;
|
||||
MeleeDamage = Max( DifficultyDamageModifer() * default.MeleeDamage, 1 );
|
||||
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
bCharging = False;
|
||||
if(Level.NetMode != NM_DedicatedServer)
|
||||
PostNetReceive();
|
||||
}
|
||||
}
|
||||
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
|
||||
super.ModDamage(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
if(damageType == class'ScrnZedPack.DamTypeEMP')
|
||||
Damage *= 0.01;
|
||||
}
|
||||
function TakeDamageClient(int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, float lockonTime){
|
||||
local bool bCanGetConfused;
|
||||
local int OldHealth;
|
||||
local PlayerController PC;
|
||||
local KFSteamStatsAndAchievements Stats;
|
||||
OldHealth = Health;
|
||||
bCanGetConfused = false;
|
||||
if(StunsRemaining != 0 && float(Health)/HealthMax >= 0.75)
|
||||
bCanGetConfused = true;
|
||||
super.takeDamageClient(Damage, instigatedBy, hitLocation, momentum, damageType, headshotLevel, lockonTime);
|
||||
if (
|
||||
bCanGetConfused &&
|
||||
!IsInState('SawingLoop') &&
|
||||
(OldHealth - Health) <= (float(default.Health)/1.5) && float(Health)/HealthMax < 0.75 &&
|
||||
(LastDamageAmount >= (0.5 * default.Health) ||
|
||||
(VSize(LastDamagedBy.Location - Location) <= (MeleeRange * 2) && ClassIsChildOf(LastDamagedbyType,class 'DamTypeMelee') &&
|
||||
KFPawn(LastDamagedBy) != none && LastDamageAmount > (0.10 * default.Health)))
|
||||
)
|
||||
bConfusedState = true;
|
||||
if(bConfusedState && Health > 0 && (headshotLevel <= 0.0) && damageType != none){
|
||||
bConfusedState = false;
|
||||
GoToState('RunningState');
|
||||
}
|
||||
if(!bConfusedState && !IsInState('SawingLoop') && !IsInState('RunningState') && float(Health) / HealthMax < 0.75)
|
||||
RangedAttack(InstigatedBy);
|
||||
if(damageType == class'DamTypeDBShotgun'){
|
||||
PC = PlayerController( InstigatedBy.Controller );
|
||||
if(PC != none){
|
||||
Stats = KFSteamStatsAndAchievements( PC.SteamStatsAndAchievements );
|
||||
if( Stats != none )
|
||||
Stats.CheckAndSetAchievementComplete( Stats.KFACHIEVEMENT_PushScrakeSPJ );
|
||||
}
|
||||
}
|
||||
}
|
||||
function TakeFireDamage(int Damage, Pawn Instigator)
|
||||
{
|
||||
Super.TakeFireDamage(Damage, Instigator);
|
||||
if(bConfusedState && Health > 0 && Damage > 150){
|
||||
bConfusedState = false;
|
||||
GoToState('RunningState');
|
||||
}
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
// Scrakes are better at enduring pain, so we need a bit more to flinch them
|
||||
if(StunsRemaining == 0 || flinchScore < 150)
|
||||
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
function DoStun(optional Pawn instigatedBy, optional Vector hitLocation, optional Vector momentum, optional class<NiceWeaponDamageType> damageType, optional float headshotLevel, optional KFPlayerReplicationInfo KFPRI){
|
||||
super.DoStun(instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
StunsRemaining = 0;
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='SawZombieAttack1' || AnimName=='SawZombieAttack2' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
Return 1;
|
||||
}
|
||||
Return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
// The animation is full body and should set the bWaitForAnim flag
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'SawImpaleLoop' || TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
if ( bGibbed )
|
||||
{
|
||||
// Do nothing for now
|
||||
PlaySound(GibGroupClass.static.GibSound(), SLOT_Pain,2.0,true,525);
|
||||
return;
|
||||
}
|
||||
|
||||
if( bDecapitated )
|
||||
{
|
||||
|
||||
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySound(DeathSound[0], SLOT_Pain,1.30,true,525);
|
||||
}
|
||||
|
||||
PlaySound(ChainSawOffSound, SLOT_Misc, 2.0,,525.0);
|
||||
}
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
AmbientSound = none;
|
||||
if ( ExhaustEffect != none )
|
||||
{
|
||||
ExhaustEffect.Destroy();
|
||||
ExhaustEffect = none;
|
||||
bNoExhaustRespawn = true;
|
||||
}
|
||||
super.Died( Killer, damageType, HitLocation );
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
|
||||
j++;
|
||||
if ( j > 30 )
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
|
||||
continue;
|
||||
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
|
||||
{
|
||||
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
|
||||
bGibbed = true;
|
||||
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
|
||||
if( Level.NetMode == NM_ListenServer )
|
||||
{
|
||||
bDestroyNextTick = true;
|
||||
TimeSetDestroyNextTickTime = Level.TimeSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
|
||||
{
|
||||
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
|
||||
{
|
||||
for( i = 0; i < ArrayCount(HitEffects); i++ )
|
||||
{
|
||||
if( HitEffects[i] == none )
|
||||
continue;
|
||||
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( class'GameInfo'.static.UseLowGore() )
|
||||
HitFX[SimHitFxTicker].bSever = false;
|
||||
|
||||
if( HitFX[SimHitFxTicker].bSever )
|
||||
{
|
||||
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
|
||||
switch( HitFX[SimHitFxTicker].bone )
|
||||
{
|
||||
case 'obliterate':
|
||||
break;
|
||||
|
||||
case LeftThighBone:
|
||||
if( !bLeftLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bLeftLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightThighBone:
|
||||
if( !bRightLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LeftFArmBone:
|
||||
if( !bLeftArmGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
|
||||
bLeftArmGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightFArmBone:
|
||||
if( !bRightArmGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedSpecialArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightArmGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'head':
|
||||
if( !bHeadGibbed )
|
||||
{
|
||||
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
|
||||
}
|
||||
|
||||
bHeadGibbed=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
|
||||
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
|
||||
HideBone(HitFX[SimHitFxTicker].bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Maybe spawn some chunks when the player gets obliterated
|
||||
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation)
|
||||
{
|
||||
if ( ExhaustEffect != none )
|
||||
{
|
||||
ExhaustEffect.Destroy();
|
||||
ExhaustEffect = none;
|
||||
bNoExhaustRespawn = true;
|
||||
}
|
||||
super.SpawnGibs(HitRotation,ChunkPerterbation);
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.scrake_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.scrake_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_saw_panner');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.scrake_FB');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.Chainsaw_blade_diff');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
SawAttackLoopSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Impale'
|
||||
ChainSawOffSound=SoundGroup'KF_ChainsawSnd.Chainsaw_Deselect'
|
||||
remainingStuns=1
|
||||
stunLoopStart=0.240000
|
||||
stunLoopEnd=0.820000
|
||||
idleInsertFrame=0.900000
|
||||
EventClasses(0)="NicePack.NiceZombieScrake"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Chainsaw_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmScrake'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegScrake'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadScrake'
|
||||
DetachedSpecialArmClass=Class'KFChar.SeveredArmScrakeSaw'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Scrake.Scrake_Challenge'
|
||||
ControllerClass=Class'NicePack.NiceZombieScrakeController'
|
||||
AmbientSound=Sound'KF_BaseScrake.Chainsaw.Scrake_Chainsaw_Idle'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.Scrake_Freak'
|
||||
Skins(0)=Shader'KF_Specimens_Trip_T.scrake_FB'
|
||||
Skins(1)=TexPanner'KF_Specimens_Trip_T.scrake_saw_panner'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,84 @@
|
|||
// Chainsaw Zombie Monster for KF Invasion gametype
|
||||
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
|
||||
class NiceZombieScrakeBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var(Sounds) sound SawAttackLoopSound; // THe sound for the saw revved up, looping
|
||||
var(Sounds) sound ChainSawOffSound; //The sound of this zombie dieing without a head
|
||||
var bool bCharging; // Scrake charges when his health gets low
|
||||
var() float AttackChargeRate; // Ratio to increase scrake movement speed when charging and attacking
|
||||
// Exhaust effects
|
||||
var() class<VehicleExhaustEffect> ExhaustEffectClass; // Effect class for the exhaust emitter
|
||||
var() VehicleExhaustEffect ExhaustEffect;
|
||||
var bool bNoExhaustRespawn;
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
bCharging;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
AttackChargeRate=2.500000
ExhaustEffectClass=Class'KFMod.ChainsawExhaust'
fuelRatio=0.400000
clientHeadshotScale=1.500000
MeleeAnims(0)="SawZombieAttack1"
MeleeAnims(1)="SawZombieAttack2"
StunsRemaining=1
BleedOutDuration=6.000000
ZapThreshold=1.250000
ZappedDamageMod=1.250000
bHarpoonToBodyStuns=False
DamageToMonsterScale=8.000000
ZombieFlag=3
MeleeDamage=20
damageForce=-75000
bFatAss=True
KFRagdollName="Scrake_Trip"
bMeleeStunImmune=True
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=55.000000)
ColRadius=29.000000
ColHeight=18.000000
SeveredArmAttachScale=1.100000
SeveredLegAttachScale=1.100000
PlayerCountHealthScale=0.500000
PoundRageBumpDamScale=0.010000
OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000)
OnlineHeadshotScale=1.500000
HeadHealth=650.000000
PlayerNumHeadHealthScale=0.300000
MotionDetectorThreat=3.000000
ScoringValue=75
IdleHeavyAnim="SawZombieIdle"
IdleRifleAnim="SawZombieIdle"
MeleeRange=40.000000
GroundSpeed=85.000000
WaterSpeed=75.000000
HealthMax=1000.000000
Health=1000
HeadHeight=2.200000
MenuName="Nice Scrake"
MovementAnims(0)="SawZombieWalk"
MovementAnims(1)="SawZombieWalk"
MovementAnims(2)="SawZombieWalk"
MovementAnims(3)="SawZombieWalk"
WalkAnims(0)="SawZombieWalk"
WalkAnims(1)="SawZombieWalk"
WalkAnims(2)="SawZombieWalk"
WalkAnims(3)="SawZombieWalk"
IdleCrouchAnim="SawZombieIdle"
IdleWeaponAnim="SawZombieIdle"
IdleRestAnim="SawZombieIdle"
DrawScale=1.050000
PrePivot=(Z=3.000000)
SoundVolume=175
SoundRadius=100.000000
Mass=500.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Chainsaw Zombie Monster for KF Invasion gametype
|
||||
// He's not quite as speedy as the other Zombies, But his attacks are TRULY damaging.
|
||||
class NiceZombieScrakeBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
var(Sounds) sound SawAttackLoopSound; // THe sound for the saw revved up, looping
|
||||
var(Sounds) sound ChainSawOffSound; //The sound of this zombie dieing without a head
|
||||
var bool bCharging; // Scrake charges when his health gets low
|
||||
var() float AttackChargeRate; // Ratio to increase scrake movement speed when charging and attacking
|
||||
// Exhaust effects
|
||||
var() class<VehicleExhaustEffect> ExhaustEffectClass; // Effect class for the exhaust emitter
|
||||
var() VehicleExhaustEffect ExhaustEffect;
|
||||
var bool bNoExhaustRespawn;
|
||||
replication
|
||||
{
|
||||
reliable if(Role == ROLE_Authority)
|
||||
bCharging;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
AttackChargeRate=2.500000
|
||||
ExhaustEffectClass=Class'KFMod.ChainsawExhaust'
|
||||
fuelRatio=0.400000
|
||||
clientHeadshotScale=1.500000
|
||||
MeleeAnims(0)="SawZombieAttack1"
|
||||
MeleeAnims(1)="SawZombieAttack2"
|
||||
StunsRemaining=1
|
||||
BleedOutDuration=6.000000
|
||||
ZapThreshold=1.250000
|
||||
ZappedDamageMod=1.250000
|
||||
bHarpoonToBodyStuns=False
|
||||
DamageToMonsterScale=8.000000
|
||||
ZombieFlag=3
|
||||
MeleeDamage=20
|
||||
damageForce=-75000
|
||||
bFatAss=True
|
||||
KFRagdollName="Scrake_Trip"
|
||||
bMeleeStunImmune=True
|
||||
Intelligence=BRAINS_Mammal
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=55.000000)
|
||||
ColRadius=29.000000
|
||||
ColHeight=18.000000
|
||||
SeveredArmAttachScale=1.100000
|
||||
SeveredLegAttachScale=1.100000
|
||||
PlayerCountHealthScale=0.500000
|
||||
PoundRageBumpDamScale=0.010000
|
||||
OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000)
|
||||
OnlineHeadshotScale=1.500000
|
||||
HeadHealth=650.000000
|
||||
PlayerNumHeadHealthScale=0.300000
|
||||
MotionDetectorThreat=3.000000
|
||||
ScoringValue=75
|
||||
IdleHeavyAnim="SawZombieIdle"
|
||||
IdleRifleAnim="SawZombieIdle"
|
||||
MeleeRange=40.000000
|
||||
GroundSpeed=85.000000
|
||||
WaterSpeed=75.000000
|
||||
HealthMax=1000.000000
|
||||
Health=1000
|
||||
HeadHeight=2.200000
|
||||
MenuName="Nice Scrake"
|
||||
MovementAnims(0)="SawZombieWalk"
|
||||
MovementAnims(1)="SawZombieWalk"
|
||||
MovementAnims(2)="SawZombieWalk"
|
||||
MovementAnims(3)="SawZombieWalk"
|
||||
WalkAnims(0)="SawZombieWalk"
|
||||
WalkAnims(1)="SawZombieWalk"
|
||||
WalkAnims(2)="SawZombieWalk"
|
||||
WalkAnims(3)="SawZombieWalk"
|
||||
IdleCrouchAnim="SawZombieIdle"
|
||||
IdleWeaponAnim="SawZombieIdle"
|
||||
IdleRestAnim="SawZombieIdle"
|
||||
DrawScale=1.050000
|
||||
PrePivot=(Z=3.000000)
|
||||
SoundVolume=175
|
||||
SoundRadius=100.000000
|
||||
Mass=500.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,63 @@
|
|||
class NiceZombieScrakeController extends NiceMonsterController;
|
||||
// Custom Zombie Thinkerating
|
||||
// By : Alex
|
||||
var bool bDoneSpottedCheck;
|
||||
// Never do that, you too cool
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Scrake saying something
if ( !KFGameType(Level.Game).bDidSpottedScrakeMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 14, "");
KFGameType(Level.Game).bDidSpottedScrakeMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
SetCombatTimer();
|
||||
else SetTimer(0.01, True);
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
Disable('NotifyBump');
Target = Enemy;
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
While( Monster(Pawn).bShotAnim )
Sleep(0.25);
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceZombieScrakeController extends NiceMonsterController;
|
||||
// Custom Zombie Thinkerating
|
||||
// By : Alex
|
||||
var bool bDoneSpottedCheck;
|
||||
// Never do that, you too cool
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
|
||||
{
|
||||
// 25% chance of first player to see this Scrake saying something
|
||||
if ( !KFGameType(Level.Game).bDidSpottedScrakeMessage && FRand() < 0.25 )
|
||||
{
|
||||
PlayerController(SeenPlayer.Controller).Speech('AUTO', 14, "");
|
||||
KFGameType(Level.Game).bDidSpottedScrakeMessage = true;
|
||||
}
|
||||
|
||||
bDoneSpottedCheck = true;
|
||||
}
|
||||
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
function TimedFireWeaponAtEnemy()
|
||||
{
|
||||
if ( (Enemy == none) || FireWeaponAt(Enemy) )
|
||||
SetCombatTimer();
|
||||
else SetTimer(0.01, True);
|
||||
}
|
||||
state ZombieCharge
|
||||
{
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function bool StrafeFromDamage(float Damage, class<DamageType> DamageType, bool bFindDest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function Timer()
|
||||
{
|
||||
Disable('NotifyBump');
|
||||
Target = Enemy;
|
||||
TimedFireWeaponAtEnemy();
|
||||
}
|
||||
WaitForAnim:
|
||||
While( Monster(Pawn).bShotAnim )
|
||||
Sleep(0.25);
|
||||
if ( !FindBestPathToward(Enemy, false,true) )
|
||||
GotoState('ZombieRestFormation');
|
||||
Moving:
|
||||
MoveToward(Enemy);
|
||||
WhatToDoNext(17);
|
||||
if ( bSoaking )
|
||||
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,221 +1,450 @@
|
|||
// Different naming scheme 'cause kf-scrntestingrounds has a stupid restriction on what zeds can be used in it's spawns
|
||||
class NiceZombieShiver extends NiceZombieShiverBase;
|
||||
var float TeleportBlockTime;
|
||||
var float HeadOffsetY;
|
||||
var transient bool bRunning, bClientRunning;
|
||||
replication
|
||||
{
|
||||
reliable if ( Role == ROLE_Authority)
bRunning;
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
super.PostNetReceive();
|
||||
if( bClientRunning != bRunning )
|
||||
{
bClientRunning = bRunning;
if( bRunning ) {
MovementAnims[0] = RunAnim;
}
else {
MovementAnims[0] = WalkAnim;
}
|
||||
}
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
{
MatAlphaSkin = ColorModifier(Level.ObjectPool.AllocateObject(class'ColorModifier'));
if (MatAlphaSkin != none)
{
MatAlphaSkin.Color = class'Canvas'.static.MakeColor(255, 255, 255, 255);
MatAlphaSkin.RenderTwoSided = false;
MatAlphaSkin.AlphaBlend = true;
MatAlphaSkin.Material = Skins[0];
Skins[0] = MatAlphaSkin;
}
|
||||
}
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if (Level.NetMode != NM_DedicatedServer && MatAlphaSkin != none)
|
||||
{
Skins[0] = default.Skins[0];
Level.ObjectPool.FreeObject(MatAlphaSkin);
|
||||
}
|
||||
Super.Destroyed();
|
||||
}
|
||||
// Overridden to add HeadOffsetY
|
||||
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
|
||||
{
|
||||
local coords C;
|
||||
local vector HeadLoc, B, M, diff;
|
||||
local float t, DotMM, Distance;
|
||||
local int look;
|
||||
local bool bUseAltHeadShotLocation;
|
||||
local bool bWasAnimating;
|
||||
if (HeadBone == '')
return False;
|
||||
// If we are a dedicated server estimate what animation is most likely playing on the client
|
||||
if (Level.NetMode == NM_DedicatedServer)
|
||||
{
if (Physics == PHYS_Falling)
PlayAnim(AirAnims[0], 1.0, 0.0);
else if (Physics == PHYS_Walking)
{
// Only play the idle anim if we're not already doing a different anim.
// This prevents anims getting interrupted on the server and borking things up - Ramm
|
||||
if( !IsAnimating(0) && !IsAnimating(1) )
{
if (bIsCrouched)
{
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
}
else
{
bUseAltHeadShotLocation=true;
}
}
else
{
bWasAnimating = true;
}
|
||||
if ( bDoTorsoTwist )
{
SmoothViewYaw = Rotation.Yaw;
SmoothViewPitch = ViewPitch;
|
||||
look = (256 * ViewPitch) & 65535;
if (look > 32768)
look -= 65536;
|
||||
SetTwistLook(0, look);
}
}
else if (Physics == PHYS_Swimming)
PlayAnim(SwimAnims[0], 1.0, 0.0);
|
||||
if( !bWasAnimating )
{
SetAnimFrame(0.5);
}
|
||||
}
|
||||
if( bUseAltHeadShotLocation )
|
||||
{
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
AdditionalScale *= OnlineHeadshotScale;
|
||||
}
|
||||
else
|
||||
{
C = GetBoneCoords(HeadBone);
|
||||
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis)
+ HeadOffsetY * C.YAxis;
|
||||
}
|
||||
//ServerHeadLocation = HeadLoc;
|
||||
// Express snipe trace line in terms of B + tM
|
||||
B = loc;
|
||||
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
|
||||
// Find Point-Line Squared Distance
|
||||
diff = HeadLoc - B;
|
||||
t = M Dot diff;
|
||||
if (t > 0)
|
||||
{
DotMM = M dot M;
if (t < DotMM)
{
t = t / DotMM;
diff = diff - (t * M);
}
else
{
t = 1;
diff -= M;
}
|
||||
}
|
||||
else
t = 0;
|
||||
Distance = Sqrt(diff Dot diff);
|
||||
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
if ( super.FlipOver() ) {
TeleportBlockTime = Level.TimeSeconds + 3.0; // can't teleport during stun
// do not rotate while stunned
Controller.Focus = none;
Controller.FocalPoint = Location + 512*vector(Rotation);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function StopBurnFX()
|
||||
{
|
||||
if (bBurnApplied)
|
||||
{
MatAlphaSkin.Material = Texture'PatchTex.Common.ZedBurnSkin';
Skins[0] = MatAlphaSkin;
|
||||
}
|
||||
Super.StopBurnFX();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if (bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if (CanAttack(A))
|
||||
{
bShotAnim = true;
SetAnimAction('Claw');
return;
|
||||
}
|
||||
}
|
||||
state Running
|
||||
{
|
||||
function Tick(float Delta)
|
||||
{
Global.Tick(Delta);
if (RunUntilTime < Level.TimeSeconds)
GotoState('');
GroundSpeed = GetOriginalGroundSpeed();
|
||||
}
|
||||
function BeginState()
|
||||
{
bRunning = true;
RunUntilTime = Level.TimeSeconds + PeriodRunBase + FRand() * PeriodRunRan;
MovementAnims[0] = RunAnim;
|
||||
}
|
||||
function EndState()
|
||||
{
bRunning = false;
GroundSpeed = global.GetOriginalGroundSpeed();
RunCooldownEnd = Level.TimeSeconds + PeriodRunCoolBase + FRand() * PeriodRunCoolRan;
MovementAnims[0] = WalkAnim;
|
||||
}
|
||||
function float GetOriginalGroundSpeed()
|
||||
{
return global.GetOriginalGroundSpeed() * 2.5;
|
||||
}
|
||||
function bool CanSpeedAdjust()
|
||||
{
return false;
|
||||
}
|
||||
}
|
||||
/*function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
|
||||
{
|
||||
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip none-reference error
|
||||
else
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
|
||||
}*/
|
||||
// returns true also for KnockDown (stun) animation -- PooSH
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
|
||||
{
return true;
|
||||
}
|
||||
return ExpectingChannel == 0;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed()
|
||||
{
|
||||
local float result;
|
||||
result = OriginalGroundSpeed;
|
||||
if( bZedUnderControl )
result *= 1.25;
return result;
|
||||
}
|
||||
simulated function HandleAnimation(float Delta)
|
||||
{
|
||||
// hehehe
|
||||
}
|
||||
simulated function Tick(float Delta)
|
||||
{
|
||||
Super.Tick(Delta);
|
||||
if (Health > 0 && !bBurnApplied)
|
||||
{
if (Level.NetMode != NM_DedicatedServer)
HandleAnimation(Delta);
// Handle targetting
if (Level.NetMode != NM_Client && !bDecapitated)
{
if (Controller == none || Controller.Target == none || !Controller.LineOfSightTo(Controller.Target))
{
if (bCanSeeTarget) bCanSeeTarget = false;
}
else
{
if (!bCanSeeTarget)
{
bCanSeeTarget = true;
SeeTargetTime = Level.TimeSeconds;
}
else if (Level.TimeSeconds > SeeTargetTime + PeriodSeeTarget)
{
if (VSize(Controller.Target.Location - Location) < MaxTeleportDist)
{
if (VSize(Controller.Target.Location - Location) > MinTeleportDist || !Controller.ActorReachable(Controller.Target))
{
if (CanTeleport())
StartTelePort();
}
else
{
if (CanRun())
GotoState('Running');
}
}
}
}
}
|
||||
}
|
||||
// Handle client-side teleport variables
|
||||
if (!bBurnApplied)
|
||||
{
if (Level.NetMode != NM_DedicatedServer && OldFadeStage != FadeStage)
{
OldFadeStage = FadeStage;
if (FadeStage == 2)
AlphaFader = 0;
else
AlphaFader = 255;
}
// Handle teleporting
if (FadeStage == 1) // Fade out (pre-teleport)
{
AlphaFader = FMax(AlphaFader - Delta * 512, 0);
|
||||
if (Level.NetMode != NM_Client && AlphaFader == 0)
{
SetCollision(true, true);
FlashTeleport();
SetCollision(false, false);
FadeStage = 2;
}
}
else if (FadeStage == 2) // Fade in (post-teleport)
{
AlphaFader = FMin(AlphaFader + Delta * 512, 255);
if (Level.NetMode != NM_Client && AlphaFader == 255)
{
FadeStage = 0;
SetCollision(true, true);
GotoState('Running');
}
}
|
||||
if (Level.NetMode != NM_DedicatedServer && ColorModifier(Skins[0]) != none)
ColorModifier(Skins[0]).Color.A = AlphaFader;
|
||||
}
|
||||
}
|
||||
//can't teleport if set on fire
|
||||
function bool CanTeleport()
|
||||
{
|
||||
return !bFlashTeleporting && !bOnFire && Physics == PHYS_Walking && Level.TimeSeconds > TeleportBlockTime
&& LastFlashTime + 7.5 < Level.TimeSeconds && !bIsStunned;
|
||||
}
|
||||
function bool CanRun()
|
||||
{
|
||||
local float distanceToTargetSquared;
|
||||
if(controller == none) return false;
|
||||
if(controller.focus != none){
distanceToTargetSquared = VSize(controller.focus.location - location);
if(distanceToTargetSquared > 900 * 2500) // (30 * 50)^2 / 30 meters
return false;
|
||||
}
|
||||
return (!bFlashTeleporting && !IsInState('Running') && RunCooldownEnd < Level.TimeSeconds);
|
||||
}
|
||||
function StartTeleport()
|
||||
{
|
||||
FadeStage = 1;
|
||||
AlphaFader = 255;
|
||||
SetCollision(false, false);
|
||||
bFlashTeleporting = true;
|
||||
}
|
||||
function FlashTeleport()
|
||||
{
|
||||
local Actor Target;
|
||||
local vector OldLoc;
|
||||
local vector NewLoc;
|
||||
local vector HitLoc;
|
||||
local vector HitNorm;
|
||||
local rotator RotOld;
|
||||
local rotator RotNew;
|
||||
local float LandTargetDist;
|
||||
local int iEndAngle;
|
||||
local int iAttempts;
|
||||
if (Controller == none || Controller.Target == none)
return;
|
||||
Target = Controller.Target;
|
||||
RotOld = rotator(Target.Location - Location);
|
||||
RotNew = RotOld;
|
||||
OldLoc = Location;
|
||||
for (iEndAngle = 0; iEndAngle < MaxTeleportAngles; iEndAngle++)
|
||||
{
RotNew = RotOld;
RotNew.Yaw += iEndAngle * (65536 / MaxTelePortAngles);
for (iAttempts = 0; iAttempts < MaxTeleportAttempts; iAttempts++)
{
LandTargetDist = Target.CollisionRadius + CollisionRadius +
MinLandDist + (MaxLandDist - MinLandDist) * (iAttempts / (MaxTeleportAttempts - 1.0));
|
||||
NewLoc = Target.Location - vector(RotNew) * LandTargetDist; // Target.Location - Location
NewLoc.Z = Target.Location.Z;
|
||||
if (Trace(HitLoc, HitNorm, NewLoc + vect(0, 0, -500), NewLoc) != none)
NewLoc.Z = HitLoc.Z + CollisionHeight;
|
||||
// Try a new location
if (SetLocation(NewLoc))
{
SetPhysics(PHYS_Walking);
if (Controller.PointReachable(Target.Location))
{
Velocity = vect(0, 0, 0);
Acceleration = vect(0, 0, 0);
SetRotation(rotator(Target.Location - Location));
PlaySound(Sound'ScrnZedPack_S.Shiver.ShiverWarpGroup', SLOT_Interact, 4.0);
Controller.GotoState('');
MonsterController(Controller).WhatToDoNext(0);
goto Teleported;
}
}
// Reset location
SetLocation(OldLoc);
}
|
||||
}
|
||||
Teleported:
|
||||
bFlashTeleporting = false;
|
||||
LastFlashTime = Level.TimeSeconds;
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
// (!)
|
||||
Super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
local class<KFWeaponDamageType> KFDamType;
|
||||
KFDamType = class<KFWeaponDamageType>(LastDamagedByType);
|
||||
if ( KFDamType != none && !KFDamType.default.bIsPowerWeapon
&& !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage
&& !KFDamType.default.bIsExplosive && !KFDamType.default.bDealBurningDamage
&& !ClassIsChildOf(KFDamType, class'DamTypeDualies')
&& !ClassIsChildOf(KFDamType, class'DamTypeMK23Pistol')
&& !ClassIsChildOf(KFDamType, class'DamTypeMagnum44Pistol') )
|
||||
{
LastDamageAmount *= 3.5; //significantly raise decapitation bonus for Assault Rifles
|
||||
//award shiver kill on decap for Commandos
if ( KFPawn(LastDamagedBy)!=none && KFPlayerController(LastDamagedBy.Controller) != none
&& KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements) != none )
{
KFDamType.Static.AwardKill(
KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements),
KFPlayerController(LastDamagedBy.Controller), self);
}
|
||||
}
|
||||
if (IsInState('Running'))
GotoState('');
|
||||
Super(NiceMonster).RemoveHead();
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(IsInState('Running'))
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if (AnimName=='Claw' || AnimName=='Claw2' || AnimName=='Claw3')
|
||||
{
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
defaultproperties
|
||||
{
HeadOffsetY=-3.000000
idleInsertFrame=0.468000
PlayerCountHealthScale=0.200000
OnlineHeadshotOffset=(X=19.000000,Z=39.000000)
ScoringValue=15
HealthMax=300.000000
Health=300
HeadRadius=8.000000
HeadHeight=3.000000
|
||||
}
|
||||
// Different naming scheme 'cause kf-scrntestingrounds has a stupid restriction on what zeds can be used in it's spawns
|
||||
class NiceZombieShiver extends NiceZombieShiverBase;
|
||||
var float TeleportBlockTime;
|
||||
var float HeadOffsetY;
|
||||
var transient bool bRunning, bClientRunning;
|
||||
replication
|
||||
{
|
||||
reliable if ( Role == ROLE_Authority)
|
||||
bRunning;
|
||||
}
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
super.PostNetReceive();
|
||||
if( bClientRunning != bRunning )
|
||||
{
|
||||
bClientRunning = bRunning;
|
||||
if( bRunning ) {
|
||||
MovementAnims[0] = RunAnim;
|
||||
}
|
||||
else {
|
||||
MovementAnims[0] = WalkAnim;
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
{
|
||||
MatAlphaSkin = ColorModifier(Level.ObjectPool.AllocateObject(class'ColorModifier'));
|
||||
if (MatAlphaSkin != none)
|
||||
{
|
||||
MatAlphaSkin.Color = class'Canvas'.static.MakeColor(255, 255, 255, 255);
|
||||
MatAlphaSkin.RenderTwoSided = false;
|
||||
MatAlphaSkin.AlphaBlend = true;
|
||||
MatAlphaSkin.Material = Skins[0];
|
||||
Skins[0] = MatAlphaSkin;
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if (Level.NetMode != NM_DedicatedServer && MatAlphaSkin != none)
|
||||
{
|
||||
Skins[0] = default.Skins[0];
|
||||
Level.ObjectPool.FreeObject(MatAlphaSkin);
|
||||
}
|
||||
Super.Destroyed();
|
||||
}
|
||||
// Overridden to add HeadOffsetY
|
||||
function bool IsHeadShot(vector loc, vector ray, float AdditionalScale)
|
||||
{
|
||||
local coords C;
|
||||
local vector HeadLoc, B, M, diff;
|
||||
local float t, DotMM, Distance;
|
||||
local int look;
|
||||
local bool bUseAltHeadShotLocation;
|
||||
local bool bWasAnimating;
|
||||
if (HeadBone == '')
|
||||
return False;
|
||||
// If we are a dedicated server estimate what animation is most likely playing on the client
|
||||
if (Level.NetMode == NM_DedicatedServer)
|
||||
{
|
||||
if (Physics == PHYS_Falling)
|
||||
PlayAnim(AirAnims[0], 1.0, 0.0);
|
||||
else if (Physics == PHYS_Walking)
|
||||
{
|
||||
// Only play the idle anim if we're not already doing a different anim.
|
||||
// This prevents anims getting interrupted on the server and borking things up - Ramm
|
||||
|
||||
if( !IsAnimating(0) && !IsAnimating(1) )
|
||||
{
|
||||
if (bIsCrouched)
|
||||
{
|
||||
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
bUseAltHeadShotLocation=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bWasAnimating = true;
|
||||
}
|
||||
|
||||
if ( bDoTorsoTwist )
|
||||
{
|
||||
SmoothViewYaw = Rotation.Yaw;
|
||||
SmoothViewPitch = ViewPitch;
|
||||
|
||||
look = (256 * ViewPitch) & 65535;
|
||||
if (look > 32768)
|
||||
look -= 65536;
|
||||
|
||||
SetTwistLook(0, look);
|
||||
}
|
||||
}
|
||||
else if (Physics == PHYS_Swimming)
|
||||
PlayAnim(SwimAnims[0], 1.0, 0.0);
|
||||
|
||||
if( !bWasAnimating )
|
||||
{
|
||||
SetAnimFrame(0.5);
|
||||
}
|
||||
}
|
||||
if( bUseAltHeadShotLocation )
|
||||
{
|
||||
HeadLoc = Location + (OnlineHeadshotOffset >> Rotation);
|
||||
AdditionalScale *= OnlineHeadshotScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
C = GetBoneCoords(HeadBone);
|
||||
|
||||
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis)
|
||||
+ HeadOffsetY * C.YAxis;
|
||||
}
|
||||
//ServerHeadLocation = HeadLoc;
|
||||
// Express snipe trace line in terms of B + tM
|
||||
B = loc;
|
||||
M = ray * (2.0 * CollisionHeight + 2.0 * CollisionRadius);
|
||||
// Find Point-Line Squared Distance
|
||||
diff = HeadLoc - B;
|
||||
t = M Dot diff;
|
||||
if (t > 0)
|
||||
{
|
||||
DotMM = M dot M;
|
||||
if (t < DotMM)
|
||||
{
|
||||
t = t / DotMM;
|
||||
diff = diff - (t * M);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 1;
|
||||
diff -= M;
|
||||
}
|
||||
}
|
||||
else
|
||||
t = 0;
|
||||
Distance = Sqrt(diff Dot diff);
|
||||
return (Distance < (HeadRadius * HeadScale * AdditionalScale));
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
if ( super.FlipOver() ) {
|
||||
TeleportBlockTime = Level.TimeSeconds + 3.0; // can't teleport during stun
|
||||
// do not rotate while stunned
|
||||
Controller.Focus = none;
|
||||
Controller.FocalPoint = Location + 512*vector(Rotation);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
simulated function StopBurnFX()
|
||||
{
|
||||
if (bBurnApplied)
|
||||
{
|
||||
MatAlphaSkin.Material = Texture'PatchTex.Common.ZedBurnSkin';
|
||||
Skins[0] = MatAlphaSkin;
|
||||
}
|
||||
Super.StopBurnFX();
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
if (bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if (CanAttack(A))
|
||||
{
|
||||
bShotAnim = true;
|
||||
SetAnimAction('Claw');
|
||||
return;
|
||||
}
|
||||
}
|
||||
state Running
|
||||
{
|
||||
function Tick(float Delta)
|
||||
{
|
||||
Global.Tick(Delta);
|
||||
if (RunUntilTime < Level.TimeSeconds)
|
||||
GotoState('');
|
||||
GroundSpeed = GetOriginalGroundSpeed();
|
||||
}
|
||||
function BeginState()
|
||||
{
|
||||
bRunning = true;
|
||||
RunUntilTime = Level.TimeSeconds + PeriodRunBase + FRand() * PeriodRunRan;
|
||||
MovementAnims[0] = RunAnim;
|
||||
}
|
||||
function EndState()
|
||||
{
|
||||
bRunning = false;
|
||||
GroundSpeed = global.GetOriginalGroundSpeed();
|
||||
RunCooldownEnd = Level.TimeSeconds + PeriodRunCoolBase + FRand() * PeriodRunCoolRan;
|
||||
MovementAnims[0] = WalkAnim;
|
||||
}
|
||||
function float GetOriginalGroundSpeed()
|
||||
{
|
||||
return global.GetOriginalGroundSpeed() * 2.5;
|
||||
}
|
||||
function bool CanSpeedAdjust()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*function TakeDamage(int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, class<DamageType> DamType, optional int HitIndex)
|
||||
{
|
||||
if (InstigatedBy == none || class<KFWeaponDamageType>(DamType) == none)
|
||||
Super(Monster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType); // skip none-reference error
|
||||
else
|
||||
Super(KFMonster).TakeDamage(Damage, instigatedBy, hitLocation, momentum, DamType);
|
||||
}*/
|
||||
// returns true also for KnockDown (stun) animation -- PooSH
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
if( TestAnim == 'DoorBash' || TestAnim == 'KnockDown' )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return ExpectingChannel == 0;
|
||||
}
|
||||
simulated function float GetOriginalGroundSpeed()
|
||||
{
|
||||
local float result;
|
||||
result = OriginalGroundSpeed;
|
||||
if( bZedUnderControl )
|
||||
result *= 1.25;
|
||||
return result;
|
||||
}
|
||||
simulated function HandleAnimation(float Delta)
|
||||
{
|
||||
// hehehe
|
||||
}
|
||||
simulated function Tick(float Delta)
|
||||
{
|
||||
Super.Tick(Delta);
|
||||
if (Health > 0 && !bBurnApplied)
|
||||
{
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
HandleAnimation(Delta);
|
||||
// Handle targetting
|
||||
if (Level.NetMode != NM_Client && !bDecapitated)
|
||||
{
|
||||
if (Controller == none || Controller.Target == none || !Controller.LineOfSightTo(Controller.Target))
|
||||
{
|
||||
if (bCanSeeTarget) bCanSeeTarget = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!bCanSeeTarget)
|
||||
{
|
||||
bCanSeeTarget = true;
|
||||
SeeTargetTime = Level.TimeSeconds;
|
||||
}
|
||||
else if (Level.TimeSeconds > SeeTargetTime + PeriodSeeTarget)
|
||||
{
|
||||
if (VSize(Controller.Target.Location - Location) < MaxTeleportDist)
|
||||
{
|
||||
if (VSize(Controller.Target.Location - Location) > MinTeleportDist || !Controller.ActorReachable(Controller.Target))
|
||||
{
|
||||
if (CanTeleport())
|
||||
StartTelePort();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CanRun())
|
||||
GotoState('Running');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle client-side teleport variables
|
||||
if (!bBurnApplied)
|
||||
{
|
||||
if (Level.NetMode != NM_DedicatedServer && OldFadeStage != FadeStage)
|
||||
{
|
||||
OldFadeStage = FadeStage;
|
||||
if (FadeStage == 2)
|
||||
AlphaFader = 0;
|
||||
else
|
||||
AlphaFader = 255;
|
||||
}
|
||||
// Handle teleporting
|
||||
if (FadeStage == 1) // Fade out (pre-teleport)
|
||||
{
|
||||
AlphaFader = FMax(AlphaFader - Delta * 512, 0);
|
||||
|
||||
if (Level.NetMode != NM_Client && AlphaFader == 0)
|
||||
{
|
||||
SetCollision(true, true);
|
||||
FlashTeleport();
|
||||
SetCollision(false, false);
|
||||
FadeStage = 2;
|
||||
}
|
||||
}
|
||||
else if (FadeStage == 2) // Fade in (post-teleport)
|
||||
{
|
||||
AlphaFader = FMin(AlphaFader + Delta * 512, 255);
|
||||
if (Level.NetMode != NM_Client && AlphaFader == 255)
|
||||
{
|
||||
FadeStage = 0;
|
||||
SetCollision(true, true);
|
||||
GotoState('Running');
|
||||
}
|
||||
}
|
||||
|
||||
if (Level.NetMode != NM_DedicatedServer && ColorModifier(Skins[0]) != none)
|
||||
ColorModifier(Skins[0]).Color.A = AlphaFader;
|
||||
}
|
||||
}
|
||||
//can't teleport if set on fire
|
||||
function bool CanTeleport()
|
||||
{
|
||||
return !bFlashTeleporting && !bOnFire && Physics == PHYS_Walking && Level.TimeSeconds > TeleportBlockTime
|
||||
&& LastFlashTime + 7.5 < Level.TimeSeconds && !bIsStunned;
|
||||
}
|
||||
function bool CanRun()
|
||||
{
|
||||
local float distanceToTargetSquared;
|
||||
if(controller == none) return false;
|
||||
if(controller.focus != none){
|
||||
distanceToTargetSquared = VSize(controller.focus.location - location);
|
||||
if(distanceToTargetSquared > 900 * 2500) // (30 * 50)^2 / 30 meters
|
||||
return false;
|
||||
}
|
||||
return (!bFlashTeleporting && !IsInState('Running') && RunCooldownEnd < Level.TimeSeconds);
|
||||
}
|
||||
function StartTeleport()
|
||||
{
|
||||
FadeStage = 1;
|
||||
AlphaFader = 255;
|
||||
SetCollision(false, false);
|
||||
bFlashTeleporting = true;
|
||||
}
|
||||
function FlashTeleport()
|
||||
{
|
||||
local Actor Target;
|
||||
local vector OldLoc;
|
||||
local vector NewLoc;
|
||||
local vector HitLoc;
|
||||
local vector HitNorm;
|
||||
local rotator RotOld;
|
||||
local rotator RotNew;
|
||||
local float LandTargetDist;
|
||||
local int iEndAngle;
|
||||
local int iAttempts;
|
||||
if (Controller == none || Controller.Target == none)
|
||||
return;
|
||||
Target = Controller.Target;
|
||||
RotOld = rotator(Target.Location - Location);
|
||||
RotNew = RotOld;
|
||||
OldLoc = Location;
|
||||
for (iEndAngle = 0; iEndAngle < MaxTeleportAngles; iEndAngle++)
|
||||
{
|
||||
RotNew = RotOld;
|
||||
RotNew.Yaw += iEndAngle * (65536 / MaxTelePortAngles);
|
||||
for (iAttempts = 0; iAttempts < MaxTeleportAttempts; iAttempts++)
|
||||
{
|
||||
LandTargetDist = Target.CollisionRadius + CollisionRadius +
|
||||
MinLandDist + (MaxLandDist - MinLandDist) * (iAttempts / (MaxTeleportAttempts - 1.0));
|
||||
|
||||
NewLoc = Target.Location - vector(RotNew) * LandTargetDist; // Target.Location - Location
|
||||
NewLoc.Z = Target.Location.Z;
|
||||
|
||||
if (Trace(HitLoc, HitNorm, NewLoc + vect(0, 0, -500), NewLoc) != none)
|
||||
NewLoc.Z = HitLoc.Z + CollisionHeight;
|
||||
|
||||
// Try a new location
|
||||
if (SetLocation(NewLoc))
|
||||
{
|
||||
SetPhysics(PHYS_Walking);
|
||||
if (Controller.PointReachable(Target.Location))
|
||||
{
|
||||
Velocity = vect(0, 0, 0);
|
||||
Acceleration = vect(0, 0, 0);
|
||||
SetRotation(rotator(Target.Location - Location));
|
||||
PlaySound(Sound'ScrnZedPack_S.Shiver.ShiverWarpGroup', SLOT_Interact, 4.0);
|
||||
Controller.GotoState('');
|
||||
MonsterController(Controller).WhatToDoNext(0);
|
||||
goto Teleported;
|
||||
}
|
||||
}
|
||||
// Reset location
|
||||
SetLocation(OldLoc);
|
||||
}
|
||||
}
|
||||
Teleported:
|
||||
bFlashTeleporting = false;
|
||||
LastFlashTime = Level.TimeSeconds;
|
||||
}
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
// (!)
|
||||
Super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
local class<KFWeaponDamageType> KFDamType;
|
||||
KFDamType = class<KFWeaponDamageType>(LastDamagedByType);
|
||||
if ( KFDamType != none && !KFDamType.default.bIsPowerWeapon
|
||||
&& !KFDamType.default.bSniperWeapon && !KFDamType.default.bIsMeleeDamage
|
||||
&& !KFDamType.default.bIsExplosive && !KFDamType.default.bDealBurningDamage
|
||||
&& !ClassIsChildOf(KFDamType, class'DamTypeDualies')
|
||||
&& !ClassIsChildOf(KFDamType, class'DamTypeMK23Pistol')
|
||||
&& !ClassIsChildOf(KFDamType, class'DamTypeMagnum44Pistol') )
|
||||
{
|
||||
LastDamageAmount *= 3.5; //significantly raise decapitation bonus for Assault Rifles
|
||||
|
||||
//award shiver kill on decap for Commandos
|
||||
if ( KFPawn(LastDamagedBy)!=none && KFPlayerController(LastDamagedBy.Controller) != none
|
||||
&& KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements) != none )
|
||||
{
|
||||
KFDamType.Static.AwardKill(
|
||||
KFSteamStatsAndAchievements(KFPlayerController(LastDamagedBy.Controller).SteamStatsAndAchievements),
|
||||
KFPlayerController(LastDamagedBy.Controller), self);
|
||||
}
|
||||
}
|
||||
if (IsInState('Running'))
|
||||
GotoState('');
|
||||
Super(NiceMonster).RemoveHead();
|
||||
}
|
||||
function bool CheckMiniFlinch(int flinchScore, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI){
|
||||
if(IsInState('Running'))
|
||||
return false;
|
||||
return super.CheckMiniFlinch(flinchScore, instigatedBy, hitLocation, momentum, damageType, headshotLevel, KFPRI);
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if (AnimName=='Claw' || AnimName=='Claw2' || AnimName=='Claw3')
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.1,, FireRootBone);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return Super.DoAnimAction(AnimName);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
HeadOffsetY=-3.000000
|
||||
idleInsertFrame=0.468000
|
||||
PlayerCountHealthScale=0.200000
|
||||
OnlineHeadshotOffset=(X=19.000000,Z=39.000000)
|
||||
ScoringValue=15
|
||||
HealthMax=300.000000
|
||||
Health=300
|
||||
HeadRadius=8.000000
|
||||
HeadHeight=3.000000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,104 @@
|
|||
class NiceZombieShiverBase extends NiceMonster;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
var name WalkAnim, RunAnim;
|
||||
// Head twitch
|
||||
var rotator CurHeadRot, NextHeadRot, HeadRot;
|
||||
var float NextHeadTime;
|
||||
var float MaxHeadTime;
|
||||
var float MaxTilt, MaxTurn;
|
||||
// Targetting, charging
|
||||
var bool bDelayedReaction;
|
||||
var bool bCanSeeTarget;
|
||||
var float SeeTargetTime;
|
||||
var float RunUntilTime;
|
||||
var float RunCooldownEnd;
|
||||
var float PeriodSeeTarget;
|
||||
var float PeriodRunBase;
|
||||
var float PeriodRunRan;
|
||||
var float PeriodRunCoolBase;
|
||||
var float PeriodRunCoolRan;
|
||||
// Teleporting
|
||||
var byte FadeStage;
|
||||
var byte OldFadeStage;
|
||||
var float AlphaFader;
|
||||
var bool bFlashTeleporting;
|
||||
var float LastFlashTime;
|
||||
var float MinTeleportDist, MaxTeleportDist;
|
||||
var float MinLandDist, MaxLandDist; // How close we can teleport to the target (collision cylinders are taken into account)
|
||||
var int MaxTeleportAttempts; // Attempts per angle
|
||||
var int MaxTeleportAngles;
|
||||
var ColorModifier MatAlphaSkin;
|
||||
replication
|
||||
{
|
||||
reliable if (Role == ROLE_Authority)
FadeStage;
|
||||
}
|
||||
defaultproperties
|
||||
{
WalkAnim="ClotWalk"
RunAnim="Run"
MaxHeadTime=0.100000
MaxTilt=10000.000000
MaxTurn=20000.000000
bDelayedReaction=True
PeriodSeeTarget=2.000000
PeriodRunBase=4.000000
PeriodRunRan=4.000000
PeriodRunCoolBase=4.000000
PeriodRunCoolRan=3.000000
AlphaFader=255.000000
MinTeleportDist=550.000000
MaxTeleportDist=2000.000000
MinLandDist=150.000000
MaxLandDist=500.000000
MaxTeleportAttempts=3
MaxTeleportAngles=3
fuelRatio=0.800000
clientHeadshotScale=1.400000
MoanVoice=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
bCannibal=True
MeleeDamage=8
damageForce=5000
KFRagdollName="Clot_Trip"
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
CrispUpThreshhold=9
PuntAnim="ClotPunt"
Intelligence=BRAINS_Mammal
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.800000
SeveredHeadAttachScale=0.800000
DetachedArmClass=Class'ScrnZedPack.SeveredArmShiver'
DetachedLegClass=Class'ScrnZedPack.SeveredLegShiver'
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadShiver'
OnlineHeadshotOffset=(X=20.000000,Z=37.000000)
OnlineHeadshotScale=1.300000
MotionDetectorThreat=0.340000
HitSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverPainGroup'
DeathSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverDeathGroup'
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
ScoringValue=7
GroundSpeed=100.000000
WaterSpeed=100.000000
AccelRate=1024.000000
JumpZ=340.000000
HealthMax=650.000000
Health=350
MenuName="Shiver"
MovementAnims(0)="ClotWalk"
AmbientSound=SoundGroup'ScrnZedPack_S.Shiver.ShiverIdleGroup'
Mesh=SkeletalMesh'ScrnZedPack_A.ShiverMesh'
DrawScale=1.100000
PrePivot=(Z=5.000000)
Skins(0)=Combiner'ScrnZedPack_T.Shiver.CmbRemoveAlpha'
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
class NiceZombieShiverBase extends NiceMonster;
|
||||
#exec load obj file=ScrnZedPack_T.utx
|
||||
#exec load obj file=ScrnZedPack_S.uax
|
||||
#exec load obj file=ScrnZedPack_A.ukx
|
||||
var name WalkAnim, RunAnim;
|
||||
// Head twitch
|
||||
var rotator CurHeadRot, NextHeadRot, HeadRot;
|
||||
var float NextHeadTime;
|
||||
var float MaxHeadTime;
|
||||
var float MaxTilt, MaxTurn;
|
||||
// Targetting, charging
|
||||
var bool bDelayedReaction;
|
||||
var bool bCanSeeTarget;
|
||||
var float SeeTargetTime;
|
||||
var float RunUntilTime;
|
||||
var float RunCooldownEnd;
|
||||
var float PeriodSeeTarget;
|
||||
var float PeriodRunBase;
|
||||
var float PeriodRunRan;
|
||||
var float PeriodRunCoolBase;
|
||||
var float PeriodRunCoolRan;
|
||||
// Teleporting
|
||||
var byte FadeStage;
|
||||
var byte OldFadeStage;
|
||||
var float AlphaFader;
|
||||
var bool bFlashTeleporting;
|
||||
var float LastFlashTime;
|
||||
var float MinTeleportDist, MaxTeleportDist;
|
||||
var float MinLandDist, MaxLandDist; // How close we can teleport to the target (collision cylinders are taken into account)
|
||||
var int MaxTeleportAttempts; // Attempts per angle
|
||||
var int MaxTeleportAngles;
|
||||
var ColorModifier MatAlphaSkin;
|
||||
replication
|
||||
{
|
||||
reliable if (Role == ROLE_Authority)
|
||||
FadeStage;
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
WalkAnim="ClotWalk"
|
||||
RunAnim="Run"
|
||||
MaxHeadTime=0.100000
|
||||
MaxTilt=10000.000000
|
||||
MaxTurn=20000.000000
|
||||
bDelayedReaction=True
|
||||
PeriodSeeTarget=2.000000
|
||||
PeriodRunBase=4.000000
|
||||
PeriodRunRan=4.000000
|
||||
PeriodRunCoolBase=4.000000
|
||||
PeriodRunCoolRan=3.000000
|
||||
AlphaFader=255.000000
|
||||
MinTeleportDist=550.000000
|
||||
MaxTeleportDist=2000.000000
|
||||
MinLandDist=150.000000
|
||||
MaxLandDist=500.000000
|
||||
MaxTeleportAttempts=3
|
||||
MaxTeleportAngles=3
|
||||
fuelRatio=0.800000
|
||||
clientHeadshotScale=1.400000
|
||||
MoanVoice=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
|
||||
bCannibal=True
|
||||
MeleeDamage=8
|
||||
damageForce=5000
|
||||
KFRagdollName="Clot_Trip"
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.clot.Clot_Jump'
|
||||
CrispUpThreshhold=9
|
||||
PuntAnim="ClotPunt"
|
||||
Intelligence=BRAINS_Mammal
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=48.000000)
|
||||
ColRadius=25.000000
|
||||
ColHeight=5.000000
|
||||
ExtCollAttachBoneName="Collision_Attach"
|
||||
SeveredArmAttachScale=0.800000
|
||||
SeveredLegAttachScale=0.800000
|
||||
SeveredHeadAttachScale=0.800000
|
||||
DetachedArmClass=Class'ScrnZedPack.SeveredArmShiver'
|
||||
DetachedLegClass=Class'ScrnZedPack.SeveredLegShiver'
|
||||
DetachedHeadClass=Class'ScrnZedPack.SeveredHeadShiver'
|
||||
OnlineHeadshotOffset=(X=20.000000,Z=37.000000)
|
||||
OnlineHeadshotScale=1.300000
|
||||
MotionDetectorThreat=0.340000
|
||||
HitSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverPainGroup'
|
||||
DeathSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverDeathGroup'
|
||||
ChallengeSound(0)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
|
||||
ChallengeSound(1)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
|
||||
ChallengeSound(2)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
|
||||
ChallengeSound(3)=SoundGroup'ScrnZedPack_S.Shiver.ShiverTalkGroup'
|
||||
ScoringValue=7
|
||||
GroundSpeed=100.000000
|
||||
WaterSpeed=100.000000
|
||||
AccelRate=1024.000000
|
||||
JumpZ=340.000000
|
||||
HealthMax=650.000000
|
||||
Health=350
|
||||
MenuName="Shiver"
|
||||
MovementAnims(0)="ClotWalk"
|
||||
AmbientSound=SoundGroup'ScrnZedPack_S.Shiver.ShiverIdleGroup'
|
||||
Mesh=SkeletalMesh'ScrnZedPack_A.ShiverMesh'
|
||||
DrawScale=1.100000
|
||||
PrePivot=(Z=5.000000)
|
||||
Skins(0)=Combiner'ScrnZedPack_T.Shiver.CmbRemoveAlpha'
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,145 +1,244 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSick extends NiceZombieSickBase;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
#exec OBJ LOAD FILE=NicePackT.utx
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var name SpitAnimation;
|
||||
var transient float NextVomitTime;
|
||||
function bool FlipOver(){
|
||||
return true;
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction(){
|
||||
if(bShotAnim)
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
return;
|
||||
else if ( A!=none )
|
||||
{
bShotAnim = true;
if( !bDecapitated && bDistanceAttackingDoor )
{
SetAnimAction('ZombieBarf');
}
else
{
SetAnimAction('DoorBash');
GotoState('DoorBashing');
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
local float ChargeChance;
|
||||
if ( bShotAnim )
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
|
||||
{
bShotAnim = true;
|
||||
// Decide what chance the bloat has of charging during a puke attack
if( Level.Game.GameDifficulty < 2.0 )
{
ChargeChance = 0.6;
}
else if( Level.Game.GameDifficulty < 4.0 )
{
ChargeChance = 0.8;
}
else if( Level.Game.GameDifficulty < 5.0 )
{
ChargeChance = 1.0;
}
else // Hardest difficulty
{
ChargeChance = 1.2;
}
|
||||
// Randomly do a moving attack so the player can't kite the zed
if( FRand() < ChargeChance )
{
SetAnimAction('ZombieBarfMoving');
RunAttackTimeout = GetAnimDuration('ZombieBarf', 0.5);
bMovingPukeAttack=true;
}
else
{
SetAnimAction('ZombieBarf');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
{
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
}
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
|
||||
if( NewAction == 'Claw' )
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.2;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ZombieBarfMoving' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
PlayAnim('ZombieBarf',, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
if ( bGibbed )
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
return;
}
|
||||
if( bDecapitated )
{
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
}
else
{
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
}
|
||||
}
|
||||
}
|
||||
|
||||
// Barf Time.
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
|
||||
if ( !SavedFireProperties.bInitialized )
|
||||
{
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
SavedFireProperties.ProjectileClass = Class'NiceSickVomit';
SavedFireProperties.WarnTargetPct = 1;
SavedFireProperties.MaxRange = 600;
SavedFireProperties.bTossed = False;
SavedFireProperties.bTrySplash = False;
SavedFireProperties.bLeadTarget = True;
SavedFireProperties.bInstantHit = True;
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
Spawn(Class'NiceSickVomit',,,FireStart,FireRotation);
|
||||
FireStart-=(0.5*CollisionRadius*Y);
|
||||
FireRotation.Yaw -= 1200;
|
||||
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
|
||||
FireStart+=(CollisionRadius*Y);
|
||||
FireRotation.Yaw += 2400;
|
||||
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
|
||||
|
||||
function BileBomb()
|
||||
{
|
||||
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
|
||||
}
|
||||
|
||||
State Dying
|
||||
{
|
||||
function tick(float deltaTime)
|
||||
{
|
||||
if (BloatJet != none)
|
||||
{
|
||||
BloatJet.SetLocation(location);
|
||||
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
|
||||
}
|
||||
super.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Texture'NicePackT.MonsterSick.Sick_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_env');
|
||||
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_cmb');
|
||||
}
|
||||
defaultproperties
|
||||
{
DetachedArmClass=Class'NicePack.NiceSeveredArmSick'
DetachedLegClass=Class'NicePack.NiceSeveredLegSick'
DetachedHeadClass=Class'NicePack.NiceSeveredHeadSick'
ControllerClass=Class'NicePack.NiceSickZombieController'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSick extends NiceZombieSickBase;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
#exec OBJ LOAD FILE=NicePackT.utx
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
var name SpitAnimation;
|
||||
var transient float NextVomitTime;
|
||||
function bool FlipOver(){
|
||||
return true;
|
||||
}
|
||||
// don't interrupt the bloat while he is puking
|
||||
simulated function bool HitCanInterruptAction(){
|
||||
if(bShotAnim)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming)
|
||||
return;
|
||||
else if ( A!=none )
|
||||
{
|
||||
bShotAnim = true;
|
||||
if( !bDecapitated && bDistanceAttackingDoor )
|
||||
{
|
||||
SetAnimAction('ZombieBarf');
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAnimAction('DoorBash');
|
||||
GotoState('DoorBashing');
|
||||
}
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
local float ChargeChance;
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
|
||||
SetAnimAction('Claw');
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
|
||||
{
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
SetAnimAction('Claw');
|
||||
//PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
|
||||
{
|
||||
bShotAnim = true;
|
||||
|
||||
// Decide what chance the bloat has of charging during a puke attack
|
||||
if( Level.Game.GameDifficulty < 2.0 )
|
||||
{
|
||||
ChargeChance = 0.6;
|
||||
}
|
||||
else if( Level.Game.GameDifficulty < 4.0 )
|
||||
{
|
||||
ChargeChance = 0.8;
|
||||
}
|
||||
else if( Level.Game.GameDifficulty < 5.0 )
|
||||
{
|
||||
ChargeChance = 1.0;
|
||||
}
|
||||
else // Hardest difficulty
|
||||
{
|
||||
ChargeChance = 1.2;
|
||||
}
|
||||
|
||||
// Randomly do a moving attack so the player can't kite the zed
|
||||
if( FRand() < ChargeChance )
|
||||
{
|
||||
SetAnimAction('ZombieBarfMoving');
|
||||
RunAttackTimeout = GetAnimDuration('ZombieBarf', 0.5);
|
||||
bMovingPukeAttack=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAnimAction('ZombieBarf');
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
|
||||
// Randomly send out a message about Bloat Vomit burning(3% chance)
|
||||
if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
|
||||
{
|
||||
PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Overridden to handle playing upper body only attacks when moving
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
local bool bWantsToAttackAndMove;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
|
||||
if( NewAction == 'Claw' )
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
if( bWantsToAttackAndMove )
|
||||
{
|
||||
ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
}
|
||||
if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.2;
|
||||
}
|
||||
}
|
||||
// Handle playing the anim action on the upper body only if we're attacking and moving
|
||||
simulated function int AttackAndMoveDoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='ZombieBarfMoving' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
|
||||
PlayAnim('ZombieBarf',, 0.1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
return super.DoAnimAction( AnimName );
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
if ( bGibbed )
|
||||
{
|
||||
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
|
||||
return;
|
||||
}
|
||||
|
||||
if( bDecapitated )
|
||||
{
|
||||
PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Barf Time.
|
||||
function SpawnTwoShots()
|
||||
{
|
||||
local vector X,Y,Z, FireStart;
|
||||
local rotator FireRotation;
|
||||
if( Controller!=none && KFDoorMover(Controller.Target)!=none )
|
||||
{
|
||||
Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
|
||||
return;
|
||||
}
|
||||
GetAxes(Rotation,X,Y,Z);
|
||||
FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
|
||||
if ( !SavedFireProperties.bInitialized )
|
||||
{
|
||||
SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
|
||||
SavedFireProperties.ProjectileClass = Class'NiceSickVomit';
|
||||
SavedFireProperties.WarnTargetPct = 1;
|
||||
SavedFireProperties.MaxRange = 600;
|
||||
SavedFireProperties.bTossed = False;
|
||||
SavedFireProperties.bTrySplash = False;
|
||||
SavedFireProperties.bLeadTarget = True;
|
||||
SavedFireProperties.bInstantHit = True;
|
||||
SavedFireProperties.bInitialized = True;
|
||||
}
|
||||
// Turn off extra collision before spawning vomit, otherwise spawn fails
|
||||
ToggleAuxCollision(false);
|
||||
FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
|
||||
Spawn(Class'NiceSickVomit',,,FireStart,FireRotation);
|
||||
FireStart-=(0.5*CollisionRadius*Y);
|
||||
FireRotation.Yaw -= 1200;
|
||||
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
|
||||
FireStart+=(CollisionRadius*Y);
|
||||
FireRotation.Yaw += 2400;
|
||||
spawn(Class'NiceSickVomit',,,FireStart, FireRotation);
|
||||
// Turn extra collision back on
|
||||
ToggleAuxCollision(true);
|
||||
}
|
||||
|
||||
|
||||
function BileBomb()
|
||||
{
|
||||
BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
|
||||
}
|
||||
|
||||
State Dying
|
||||
{
|
||||
function tick(float deltaTime)
|
||||
{
|
||||
if (BloatJet != none)
|
||||
{
|
||||
BloatJet.SetLocation(location);
|
||||
BloatJet.SetRotation(GetBoneRotation(FireRootBone));
|
||||
}
|
||||
super.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
bCanDistanceAttackDoors = False;
|
||||
Super.RemoveHead();
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Texture'NicePackT.MonsterSick.Sick_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_env');
|
||||
myLevel.AddPrecacheMaterial(Combiner'NicePackT.MonsterSick.Sick_cmb');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
DetachedArmClass=Class'NicePack.NiceSeveredArmSick'
|
||||
DetachedLegClass=Class'NicePack.NiceSeveredLegSick'
|
||||
DetachedHeadClass=Class'NicePack.NiceSeveredHeadSick'
|
||||
ControllerClass=Class'NicePack.NiceSickZombieController'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,100 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSickBase extends NiceMonster;
|
||||
var name WalkAnim, RunAnim;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
var BileJet BloatJet;
|
||||
var bool bPlayBileSplash;
|
||||
var bool bMovingPukeAttack;
|
||||
var float RunAttackTimeout;
|
||||
var() float AttackChargeRate;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
AttackChargeRate=2.500000
StunThreshold=4.000000
fuelRatio=0.250000
bWeakHead=True
clientHeadshotScale=1.500000
MeleeAnims(0)="Strike"
MeleeAnims(1)="Strike"
MeleeAnims(2)="Strike"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
BleedOutDuration=6.000000
ZombieFlag=3
MeleeDamage=14
damageForce=70000
bFatAss=True
KFRagdollName="Clot_Trip"
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
PuntAnim="BloatPunt"
Intelligence=BRAINS_Stupid
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=55.000000)
ColRadius=29.000000
ColHeight=18.000000
SeveredArmAttachScale=1.400000
SeveredLegAttachScale=1.400000
SeveredHeadAttachScale=1.400000
PlayerCountHealthScale=0.250000
HeadlessWalkAnims(0)="WalkF"
HeadlessWalkAnims(1)="WalkB"
HeadlessWalkAnims(2)="WalkL"
HeadlessWalkAnims(3)="WalkR"
BurningWalkFAnims(0)="WalkF"
BurningWalkFAnims(1)="WalkF"
BurningWalkFAnims(2)="WalkF"
BurningWalkAnims(0)="WalkB"
BurningWalkAnims(1)="WalkL"
BurningWalkAnims(2)="WalkR"
PoundRageBumpDamScale=0.010000
OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000)
HeadHealth=50.000000
MotionDetectorThreat=3.000000
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
AmmunitionClass=Class'KFMod.BZombieAmmo'
ScoringValue=75
IdleHeavyAnim="Idle"
IdleRifleAnim="Idle"
RagdollLifeSpan=20.000000
RagDeathVel=150.000000
RagShootStrength=300.000000
RagSpinScale=12.500000
RagDeathUpKick=50.000000
MeleeRange=30.000000
GroundSpeed=175.000000
WaterSpeed=150.000000
HealthMax=925.000000
Health=925
HeadHeight=2.200000
HeadScale=1.610000
AmbientSoundScaling=8.000000
MenuName="Sick"
MovementAnims(0)="WalkF"
MovementAnims(1)="WalkB"
MovementAnims(2)="WalkL"
MovementAnims(3)="WalkR"
WalkAnims(1)="WalkB"
WalkAnims(2)="WalkL"
WalkAnims(3)="WalkR"
IdleCrouchAnim="Idle"
IdleWeaponAnim="Idle"
IdleRestAnim="Idle"
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
Mesh=SkeletalMesh'NicePackA.MonsterSick.Sick'
DrawScale=1.100000
PrePivot=(Z=5.000000)
Skins(0)=Combiner'NicePackT.MonsterSick.Sick_cmb'
SoundVolume=200
Mass=400.000000
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSickBase extends NiceMonster;
|
||||
var name WalkAnim, RunAnim;
|
||||
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
|
||||
var BileJet BloatJet;
|
||||
var bool bPlayBileSplash;
|
||||
var bool bMovingPukeAttack;
|
||||
var float RunAttackTimeout;
|
||||
var() float AttackChargeRate;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
AttackChargeRate=2.500000
|
||||
StunThreshold=4.000000
|
||||
fuelRatio=0.250000
|
||||
bWeakHead=True
|
||||
clientHeadshotScale=1.500000
|
||||
MeleeAnims(0)="Strike"
|
||||
MeleeAnims(1)="Strike"
|
||||
MeleeAnims(2)="Strike"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Talk'
|
||||
BleedOutDuration=6.000000
|
||||
ZombieFlag=3
|
||||
MeleeDamage=14
|
||||
damageForce=70000
|
||||
bFatAss=True
|
||||
KFRagdollName="Clot_Trip"
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Jump'
|
||||
PuntAnim="BloatPunt"
|
||||
Intelligence=BRAINS_Stupid
|
||||
bCanDistanceAttackDoors=True
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=55.000000)
|
||||
ColRadius=29.000000
|
||||
ColHeight=18.000000
|
||||
SeveredArmAttachScale=1.400000
|
||||
SeveredLegAttachScale=1.400000
|
||||
SeveredHeadAttachScale=1.400000
|
||||
PlayerCountHealthScale=0.250000
|
||||
HeadlessWalkAnims(0)="WalkF"
|
||||
HeadlessWalkAnims(1)="WalkB"
|
||||
HeadlessWalkAnims(2)="WalkL"
|
||||
HeadlessWalkAnims(3)="WalkR"
|
||||
BurningWalkFAnims(0)="WalkF"
|
||||
BurningWalkFAnims(1)="WalkF"
|
||||
BurningWalkFAnims(2)="WalkF"
|
||||
BurningWalkAnims(0)="WalkB"
|
||||
BurningWalkAnims(1)="WalkL"
|
||||
BurningWalkAnims(2)="WalkR"
|
||||
PoundRageBumpDamScale=0.010000
|
||||
OnlineHeadshotOffset=(X=22.000000,Y=5.000000,Z=58.000000)
|
||||
HeadHealth=50.000000
|
||||
MotionDetectorThreat=3.000000
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.GoreFast.Gorefast_Challenge'
|
||||
AmmunitionClass=Class'KFMod.BZombieAmmo'
|
||||
ScoringValue=75
|
||||
IdleHeavyAnim="Idle"
|
||||
IdleRifleAnim="Idle"
|
||||
RagdollLifeSpan=20.000000
|
||||
RagDeathVel=150.000000
|
||||
RagShootStrength=300.000000
|
||||
RagSpinScale=12.500000
|
||||
RagDeathUpKick=50.000000
|
||||
MeleeRange=30.000000
|
||||
GroundSpeed=175.000000
|
||||
WaterSpeed=150.000000
|
||||
HealthMax=925.000000
|
||||
Health=925
|
||||
HeadHeight=2.200000
|
||||
HeadScale=1.610000
|
||||
AmbientSoundScaling=8.000000
|
||||
MenuName="Sick"
|
||||
MovementAnims(0)="WalkF"
|
||||
MovementAnims(1)="WalkB"
|
||||
MovementAnims(2)="WalkL"
|
||||
MovementAnims(3)="WalkR"
|
||||
WalkAnims(1)="WalkB"
|
||||
WalkAnims(2)="WalkL"
|
||||
WalkAnims(3)="WalkR"
|
||||
IdleCrouchAnim="Idle"
|
||||
IdleWeaponAnim="Idle"
|
||||
IdleRestAnim="Idle"
|
||||
AmbientSound=Sound'KF_BaseGorefast.Gorefast_Idle'
|
||||
Mesh=SkeletalMesh'NicePackA.MonsterSick.Sick'
|
||||
DrawScale=1.100000
|
||||
PrePivot=(Z=5.000000)
|
||||
Skins(0)=Combiner'NicePackT.MonsterSick.Sick_cmb'
|
||||
SoundVolume=200
|
||||
Mass=400.000000
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,194 +1,455 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSiren extends NiceZombieSirenBase;
|
||||
var float screamLength;
|
||||
var float screamStartTime;
|
||||
var int currScreamTiming;
|
||||
var int currentScreamID;
|
||||
var array<float> screamTimings;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
screamLength = GetAnimDuration('Siren_Scream');
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
meleeAnimIndex = Rand(3);
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
AnimAction = NewAction;
bResetAnimAct = True;
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming || bDecapitated || A==none )
return;
|
||||
bShotAnim = true;
|
||||
SetAnimAction('Siren_Scream');
|
||||
}
|
||||
function MakeNewScreamBall(){
|
||||
local int i;
|
||||
local NicePack niceMut;
|
||||
if(screamStartTime > 0){
niceMut = class'NicePack'.static.Myself(Level);
if(niceMut != none){
for(i = 0;i < niceMut.playersList.Length;i ++)
if(niceMut.playersList[i] != none && screamStartTime > 0)
niceMut.playersList[i].SpawnSirenBall(self);
}
|
||||
}
|
||||
}
|
||||
function DiscardCurrentScreamBall(){
|
||||
local int i;
|
||||
local NicePack niceMut;
|
||||
if(screamStartTime > 0){
niceMut = class'NicePack'.static.Myself(Level);
if(niceMut != none){
for(i = 0;i < niceMut.playersList.Length;i ++)
if(niceMut.playersList[i] != none)
niceMut.playersList[i].ClientRemoveSirenBall(currentScreamID);
}
screamStartTime = -1.0;
currScreamTiming = -1;
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
local float Dist;
|
||||
if ( bShotAnim )
return;
|
||||
Dist = VSize(A.Location - Location);
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
SetAnimAction('Claw');
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if(Dist < MeleeRange + CollisionRadius + A.CollisionRadius && A != Self)
|
||||
{
bShotAnim = true;
LastFireTime = Level.TimeSeconds;
SetAnimAction('Claw');
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if( Dist <= ScreamRadius && !bDecapitated && !bZapped )
|
||||
{
bShotAnim=true;
SetAnimAction('Siren_Scream');
if(screamStartTime > 0)
DiscardCurrentScreamBall();
currScreamTiming = 0;
screamStartTime = Level.TimeSeconds;
// Only stop moving if we are close
if( Dist < ScreamRadius * 0.25 )
{
Controller.bPreparingMove = true;
Acceleration = vect(0,0,0);
}
else
{
Acceleration = AccelRate * Normal(A.Location - Location);
}
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='Siren_Scream' || AnimName=='Siren_Bite' || AnimName=='Siren_Bite2' )
|
||||
{
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
PlayAnim(AnimName,, 0.1, 1);
return 1;
|
||||
}
|
||||
PlayAnim(AnimName,,0.1);
|
||||
Return 0;
|
||||
}
|
||||
// Scream Time
|
||||
simulated function SpawnTwoShots(){
|
||||
if(bZapped)
return;
|
||||
if(Health > 0 && HeadHealth > 0 && !bIsStunned)
DoShakeEffect();
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
// Deal Actual Damage.
if(controller!=none && KFDoorMover(Controller.Target) != none)
Controller.Target.TakeDamage(ScreamDamage*0.6,Self,Location,vect(0,0,0),ScreamDamageType);
else HurtRadius(ScreamDamage ,ScreamRadius, ScreamDamageType, ScreamForce, Location);
if(screamStartTime > 0)
currScreamTiming ++;
else
Log("ERROR: unexpected siren scream happend!");
|
||||
}
|
||||
}
|
||||
// Shake nearby players screens
|
||||
simulated function DoShakeEffect()
|
||||
{
|
||||
local PlayerController PC;
|
||||
local NicePlayerController nicePlayer;
|
||||
local float Dist, scale, BlurScale;
|
||||
//viewshake
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
{
PC = Level.GetLocalPlayerController();
|
||||
nicePlayer = NicePlayerController(PC);
if (PC != none && PC.ViewTarget != none)
{
Dist = VSize(Location - PC.ViewTarget.Location);
if (Dist < ScreamRadius )
{
scale = (ScreamRadius - Dist) / (ScreamRadius);
scale *= ShakeEffectScalar;
|
||||
if(nicePlayer != none)
|
||||
scale *= nicePlayer.sirenScreamMod;
|
||||
BlurScale = scale;
|
||||
// Reduce blur if there is something between us and the siren
if( !FastTrace(PC.ViewTarget.Location,Location) )
{
scale *= 0.25;
BlurScale = scale;
}
else
{
if(nicePlayer != none)
|
||||
scale = Lerp(scale, MinShakeEffectScale * nicePlayer.sirenScreamMod, 1.0);
|
||||
else
|
||||
scale = Lerp(scale, MinShakeEffectScale, 1.0);
}
|
||||
PC.SetAmbientShake(Level.TimeSeconds + ShakeFadeTime, ShakeTime, OffsetMag * Scale, OffsetRate, RotMag * Scale, RotRate);
|
||||
if( KFHumanPawn(PC.ViewTarget) != none )
{
KFHumanPawn(PC.ViewTarget).AddBlur(ShakeTime, BlurScale * ScreamBlurScale);
}
|
||||
// 10% chance of player saying something about our scream
if ( Level != none && Level.Game != none && !KFGameType(Level.Game).bDidSirenScreamMessage && FRand() < 0.10 )
{
PC.Speech('AUTO', 16, "");
KFGameType(Level.Game).bDidSirenScreamMessage = true;
}
}
}
|
||||
}
|
||||
}
|
||||
simulated function HurtRadius(float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation)
|
||||
{
|
||||
local actor Victims;
|
||||
local float InitMomentum;
|
||||
local float damageScale, dist;
|
||||
local vector dir;
|
||||
local float UsedDamageAmount;
|
||||
local KFHumanPawn humanPawn;
|
||||
local class<NiceVeterancyTypes> niceVet;
|
||||
if(bHurtEntry || Health <= 0 || HeadHealth <= 0 || bIsStunned)
return;
|
||||
bHurtEntry = true;
|
||||
InitMomentum = Momentum;
|
||||
if(screamStartTime > 0 && currScreamTiming == 0)
MakeNewScreamBall();
|
||||
foreach VisibleCollidingActors(class 'Actor', Victims, DamageRadius, HitLocation){
Momentum = InitMomentum;
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
// Or Karma actors in this case. Self inflicted Death due to flying chairs is uncool for a zombie of your stature.
if((Victims != self) && !Victims.IsA('FluidSurfaceInfo') && !Victims.IsA('KFMonster') && !Victims.IsA('ExtendedZCollision')){
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
humanPawn = KFHumanPawn(Victims);
if(humanPawn == none) // If it aint human, don't pull the vortex crap on it.
Momentum = 0;
else{ // Also don't do it if we're sharpshooter with a right skill
niceVet = class'NiceVeterancyTypes'.static.GetVeterancy(humanPawn.PlayerReplicationInfo);
if(niceVet != none && !niceVet.static.CanBePulled(KFPlayerReplicationInfo(humanPawn.PlayerReplicationInfo)))
Momentum = 0;
}
|
||||
if(Victims.IsA('KFGlassMover')) // Hack for shattering in interesting ways.
UsedDamageAmount = 100000; // Siren always shatters glass
else
UsedDamageAmount = DamageAmount;
|
||||
Victims.TakeDamage(damageScale * UsedDamageAmount,Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir, (damageScale * Momentum * dir), DamageType);
|
||||
if (Instigator != none && Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(UsedDamageAmount, DamageRadius, Instigator.Controller, DamageType, Momentum, HitLocation);
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
// When siren loses her head she's got nothin' Kill her.
|
||||
function RemoveHead(){
|
||||
Super.RemoveHead();
|
||||
}
|
||||
simulated function Tick( float Delta )
|
||||
{
|
||||
local float currScreamTime;
|
||||
Super.Tick(Delta);
|
||||
if( bAboutToDie && Level.TimeSeconds>DeathTimer )
|
||||
{
if( Health>0 && Level.NetMode!=NM_Client )
KilledBy(LastDamagedBy);
bAboutToDie = False;
|
||||
}
|
||||
if( Role == ROLE_Authority )
|
||||
{
if( bShotAnim )
{
SetGroundSpeed(GetOriginalGroundSpeed() * 0.65);
|
||||
if( LookTarget!=none )
{
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
}
}
else
{
SetGroundSpeed(GetOriginalGroundSpeed());
}
|
||||
}
|
||||
if(Role == ROLE_Authority && screamStartTime > 0){
currScreamTime = Level.TimeSeconds - screamStartTime;
if(currScreamTiming >= screamTimings.Length ||
currScreamTime - 0.1 > screamTimings[currScreamTiming] * screamLength){
DiscardCurrentScreamBall();
}
|
||||
}
|
||||
if(bOnFire && !bShotAnim)
RangedAttack(Self);
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( !bAboutToDie )
Super.PlayDyingSound();
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
SimHitFxTicker = HitFxTicker;
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
j++;
if ( j > 30 )
{
SimHitFxTicker = HitFxTicker;
return;
}
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
continue;
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
{
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
bGibbed = true;
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
if( Level.NetMode == NM_ListenServer )
{
bDestroyNextTick = true;
TimeSetDestroyNextTickTime = Level.TimeSeconds;
}
else
{
Destroy();
}
return;
}
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore())
{
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
{
for( i = 0; i < ArrayCount(HitEffects); i++ )
{
if( HitEffects[i] == none )
continue;
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
}
}
}
if ( class'GameInfo'.static.UseLowGore() )
HitFX[SimHitFxTicker].bSever = false;
|
||||
if( HitFX[SimHitFxTicker].bSever )
{
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
switch( HitFX[SimHitFxTicker].bone )
{
case 'obliterate':
break;
|
||||
case LeftThighBone:
if( !bLeftLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bLeftLegGibbed=true;
}
break;
|
||||
case RightThighBone:
if( !bRightLegGibbed )
{
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
bRightLegGibbed=true;
}
break;
|
||||
case LeftFArmBone:
break;
|
||||
case RightFArmBone:
break;
|
||||
case 'head':
if( !bHeadGibbed )
{
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
}
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
{
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
}
|
||||
bHeadGibbed=true;
}
break;
}
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
HideBone(HitFX[SimHitFxTicker].bone);
}
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_hair');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.siren_hair_fb');
|
||||
}
|
||||
defaultproperties
|
||||
{
screamTimings(0)=0.420000
screamTimings(1)=0.510000
screamTimings(2)=0.590000
screamTimings(3)=0.670000
screamTimings(4)=0.760000
screamTimings(5)=0.840000
stunLoopStart=0.200000
stunLoopEnd=0.820000
idleInsertFrame=0.920000
EventClasses(0)="NicePack.NiceZombieSiren"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Talk'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Jump'
DetachedLegClass=Class'KFChar.SeveredLegSiren'
DetachedHeadClass=Class'KFChar.SeveredHeadSiren'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Death'
ControllerClass=Class'NicePack.NiceZombieSirenController'
AmbientSound=Sound'KF_BaseSiren.Siren_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Siren_Freak'
Skins(0)=FinalBlend'KF_Specimens_Trip_T.siren_hair_fb'
Skins(1)=Combiner'KF_Specimens_Trip_T.siren_cmb'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSiren extends NiceZombieSirenBase;
|
||||
var float screamLength;
|
||||
var float screamStartTime;
|
||||
var int currScreamTiming;
|
||||
var int currentScreamID;
|
||||
var array<float> screamTimings;
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
screamLength = GetAnimDuration('Siren_Scream');
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
local int meleeAnimIndex;
|
||||
if( NewAction=='' )
|
||||
Return;
|
||||
if(NewAction == 'Claw')
|
||||
{
|
||||
meleeAnimIndex = Rand(3);
|
||||
NewAction = meleeAnims[meleeAnimIndex];
|
||||
}
|
||||
ExpectingChannel = DoAnimAction(NewAction);
|
||||
if( AnimNeedsWait(NewAction) )
|
||||
{
|
||||
bWaitForAnim = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitForAnim = false;
|
||||
}
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
AnimAction = NewAction;
|
||||
bResetAnimAct = True;
|
||||
ResetAnimActTime = Level.TimeSeconds+0.3;
|
||||
}
|
||||
}
|
||||
simulated function bool AnimNeedsWait(name TestAnim)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
function bool FlipOver()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
function DoorAttack(Actor A)
|
||||
{
|
||||
if ( bShotAnim || Physics == PHYS_Swimming || bDecapitated || A==none )
|
||||
return;
|
||||
bShotAnim = true;
|
||||
SetAnimAction('Siren_Scream');
|
||||
}
|
||||
function MakeNewScreamBall(){
|
||||
local int i;
|
||||
local NicePack niceMut;
|
||||
if(screamStartTime > 0){
|
||||
niceMut = class'NicePack'.static.Myself(Level);
|
||||
if(niceMut != none){
|
||||
for(i = 0;i < niceMut.playersList.Length;i ++)
|
||||
if(niceMut.playersList[i] != none && screamStartTime > 0)
|
||||
niceMut.playersList[i].SpawnSirenBall(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
function DiscardCurrentScreamBall(){
|
||||
local int i;
|
||||
local NicePack niceMut;
|
||||
if(screamStartTime > 0){
|
||||
niceMut = class'NicePack'.static.Myself(Level);
|
||||
if(niceMut != none){
|
||||
for(i = 0;i < niceMut.playersList.Length;i ++)
|
||||
if(niceMut.playersList[i] != none)
|
||||
niceMut.playersList[i].ClientRemoveSirenBall(currentScreamID);
|
||||
}
|
||||
screamStartTime = -1.0;
|
||||
currScreamTiming = -1;
|
||||
}
|
||||
}
|
||||
function RangedAttack(Actor A)
|
||||
{
|
||||
local int LastFireTime;
|
||||
local float Dist;
|
||||
if ( bShotAnim )
|
||||
return;
|
||||
Dist = VSize(A.Location - Location);
|
||||
if ( Physics == PHYS_Swimming )
|
||||
{
|
||||
SetAnimAction('Claw');
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
}
|
||||
else if(Dist < MeleeRange + CollisionRadius + A.CollisionRadius && A != Self)
|
||||
{
|
||||
bShotAnim = true;
|
||||
LastFireTime = Level.TimeSeconds;
|
||||
SetAnimAction('Claw');
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else if( Dist <= ScreamRadius && !bDecapitated && !bZapped )
|
||||
{
|
||||
bShotAnim=true;
|
||||
SetAnimAction('Siren_Scream');
|
||||
if(screamStartTime > 0)
|
||||
DiscardCurrentScreamBall();
|
||||
currScreamTiming = 0;
|
||||
screamStartTime = Level.TimeSeconds;
|
||||
// Only stop moving if we are close
|
||||
if( Dist < ScreamRadius * 0.25 )
|
||||
{
|
||||
Controller.bPreparingMove = true;
|
||||
Acceleration = vect(0,0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Acceleration = AccelRate * Normal(A.Location - Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function int DoAnimAction( name AnimName )
|
||||
{
|
||||
if( AnimName=='Siren_Scream' || AnimName=='Siren_Bite' || AnimName=='Siren_Bite2' )
|
||||
{
|
||||
AnimBlendParams(1, 1.0, 0.0,, SpineBone1);
|
||||
PlayAnim(AnimName,, 0.1, 1);
|
||||
return 1;
|
||||
}
|
||||
PlayAnim(AnimName,,0.1);
|
||||
Return 0;
|
||||
}
|
||||
// Scream Time
|
||||
simulated function SpawnTwoShots(){
|
||||
if(bZapped)
|
||||
return;
|
||||
if(Health > 0 && HeadHealth > 0 && !bIsStunned)
|
||||
DoShakeEffect();
|
||||
if( Level.NetMode!=NM_Client )
|
||||
{
|
||||
// Deal Actual Damage.
|
||||
if(controller!=none && KFDoorMover(Controller.Target) != none)
|
||||
Controller.Target.TakeDamage(ScreamDamage*0.6,Self,Location,vect(0,0,0),ScreamDamageType);
|
||||
else HurtRadius(ScreamDamage ,ScreamRadius, ScreamDamageType, ScreamForce, Location);
|
||||
if(screamStartTime > 0)
|
||||
currScreamTiming ++;
|
||||
else
|
||||
Log("ERROR: unexpected siren scream happend!");
|
||||
}
|
||||
}
|
||||
// Shake nearby players screens
|
||||
simulated function DoShakeEffect()
|
||||
{
|
||||
local PlayerController PC;
|
||||
local NicePlayerController nicePlayer;
|
||||
local float Dist, scale, BlurScale;
|
||||
//viewshake
|
||||
if (Level.NetMode != NM_DedicatedServer)
|
||||
{
|
||||
PC = Level.GetLocalPlayerController();
|
||||
nicePlayer = NicePlayerController(PC);
|
||||
if (PC != none && PC.ViewTarget != none)
|
||||
{
|
||||
Dist = VSize(Location - PC.ViewTarget.Location);
|
||||
if (Dist < ScreamRadius )
|
||||
{
|
||||
scale = (ScreamRadius - Dist) / (ScreamRadius);
|
||||
scale *= ShakeEffectScalar;
|
||||
if(nicePlayer != none)
|
||||
scale *= nicePlayer.sirenScreamMod;
|
||||
|
||||
BlurScale = scale;
|
||||
|
||||
// Reduce blur if there is something between us and the siren
|
||||
if( !FastTrace(PC.ViewTarget.Location,Location) )
|
||||
{
|
||||
scale *= 0.25;
|
||||
BlurScale = scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(nicePlayer != none)
|
||||
scale = Lerp(scale, MinShakeEffectScale * nicePlayer.sirenScreamMod, 1.0);
|
||||
else
|
||||
scale = Lerp(scale, MinShakeEffectScale, 1.0);
|
||||
}
|
||||
|
||||
PC.SetAmbientShake(Level.TimeSeconds + ShakeFadeTime, ShakeTime, OffsetMag * Scale, OffsetRate, RotMag * Scale, RotRate);
|
||||
|
||||
if( KFHumanPawn(PC.ViewTarget) != none )
|
||||
{
|
||||
KFHumanPawn(PC.ViewTarget).AddBlur(ShakeTime, BlurScale * ScreamBlurScale);
|
||||
}
|
||||
|
||||
// 10% chance of player saying something about our scream
|
||||
if ( Level != none && Level.Game != none && !KFGameType(Level.Game).bDidSirenScreamMessage && FRand() < 0.10 )
|
||||
{
|
||||
PC.Speech('AUTO', 16, "");
|
||||
KFGameType(Level.Game).bDidSirenScreamMessage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated function HurtRadius(float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation)
|
||||
{
|
||||
local actor Victims;
|
||||
local float InitMomentum;
|
||||
local float damageScale, dist;
|
||||
local vector dir;
|
||||
local float UsedDamageAmount;
|
||||
local KFHumanPawn humanPawn;
|
||||
local class<NiceVeterancyTypes> niceVet;
|
||||
if(bHurtEntry || Health <= 0 || HeadHealth <= 0 || bIsStunned)
|
||||
return;
|
||||
bHurtEntry = true;
|
||||
InitMomentum = Momentum;
|
||||
if(screamStartTime > 0 && currScreamTiming == 0)
|
||||
MakeNewScreamBall();
|
||||
foreach VisibleCollidingActors(class 'Actor', Victims, DamageRadius, HitLocation){
|
||||
Momentum = InitMomentum;
|
||||
// don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag
|
||||
// Or Karma actors in this case. Self inflicted Death due to flying chairs is uncool for a zombie of your stature.
|
||||
if((Victims != self) && !Victims.IsA('FluidSurfaceInfo') && !Victims.IsA('KFMonster') && !Victims.IsA('ExtendedZCollision')){
|
||||
dir = Victims.Location - HitLocation;
|
||||
dist = FMax(1,VSize(dir));
|
||||
dir = dir/dist;
|
||||
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
|
||||
humanPawn = KFHumanPawn(Victims);
|
||||
if(humanPawn == none) // If it aint human, don't pull the vortex crap on it.
|
||||
Momentum = 0;
|
||||
else{ // Also don't do it if we're sharpshooter with a right skill
|
||||
niceVet = class'NiceVeterancyTypes'.static.GetVeterancy(humanPawn.PlayerReplicationInfo);
|
||||
if(niceVet != none && !niceVet.static.CanBePulled(KFPlayerReplicationInfo(humanPawn.PlayerReplicationInfo)))
|
||||
Momentum = 0;
|
||||
}
|
||||
|
||||
if(Victims.IsA('KFGlassMover')) // Hack for shattering in interesting ways.
|
||||
UsedDamageAmount = 100000; // Siren always shatters glass
|
||||
else
|
||||
UsedDamageAmount = DamageAmount;
|
||||
|
||||
Victims.TakeDamage(damageScale * UsedDamageAmount,Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir, (damageScale * Momentum * dir), DamageType);
|
||||
|
||||
if (Instigator != none && Vehicle(Victims) != none && Vehicle(Victims).Health > 0)
|
||||
Vehicle(Victims).DriverRadiusDamage(UsedDamageAmount, DamageRadius, Instigator.Controller, DamageType, Momentum, HitLocation);
|
||||
}
|
||||
}
|
||||
bHurtEntry = false;
|
||||
}
|
||||
// When siren loses her head she's got nothin' Kill her.
|
||||
function RemoveHead(){
|
||||
Super.RemoveHead();
|
||||
}
|
||||
simulated function Tick( float Delta )
|
||||
{
|
||||
local float currScreamTime;
|
||||
Super.Tick(Delta);
|
||||
if( bAboutToDie && Level.TimeSeconds>DeathTimer )
|
||||
{
|
||||
if( Health>0 && Level.NetMode!=NM_Client )
|
||||
KilledBy(LastDamagedBy);
|
||||
bAboutToDie = False;
|
||||
}
|
||||
if( Role == ROLE_Authority )
|
||||
{
|
||||
if( bShotAnim )
|
||||
{
|
||||
SetGroundSpeed(GetOriginalGroundSpeed() * 0.65);
|
||||
|
||||
if( LookTarget!=none )
|
||||
{
|
||||
Acceleration = AccelRate * Normal(LookTarget.Location - Location);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGroundSpeed(GetOriginalGroundSpeed());
|
||||
}
|
||||
}
|
||||
if(Role == ROLE_Authority && screamStartTime > 0){
|
||||
currScreamTime = Level.TimeSeconds - screamStartTime;
|
||||
if(currScreamTiming >= screamTimings.Length ||
|
||||
currScreamTime - 0.1 > screamTimings[currScreamTiming] * screamLength){
|
||||
DiscardCurrentScreamBall();
|
||||
}
|
||||
}
|
||||
if(bOnFire && !bShotAnim)
|
||||
RangedAttack(Self);
|
||||
}
|
||||
function PlayDyingSound()
|
||||
{
|
||||
if( !bAboutToDie )
|
||||
Super.PlayDyingSound();
|
||||
}
|
||||
simulated function ProcessHitFX()
|
||||
{
|
||||
local Coords boneCoords;
|
||||
local class<xEmitter> HitEffects[4];
|
||||
local int i,j;
|
||||
local float GibPerterbation;
|
||||
if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
|
||||
{
|
||||
j++;
|
||||
if ( j > 30 )
|
||||
{
|
||||
SimHitFxTicker = HitFxTicker;
|
||||
return;
|
||||
}
|
||||
|
||||
if( (HitFX[SimHitFxTicker].damtype == none) || (Level.bDropDetail && (Level.TimeSeconds - LastRenderTime > 3) && !IsHumanControlled()) )
|
||||
continue;
|
||||
|
||||
//log("Processing effects for damtype "$HitFX[SimHitFxTicker].damtype);
|
||||
|
||||
if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
|
||||
{
|
||||
SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
|
||||
bGibbed = true;
|
||||
// Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
|
||||
if( Level.NetMode == NM_ListenServer )
|
||||
{
|
||||
bDestroyNextTick = true;
|
||||
TimeSetDestroyNextTickTime = Level.TimeSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );
|
||||
|
||||
if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore())
|
||||
{
|
||||
//AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
|
||||
HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );
|
||||
|
||||
if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
|
||||
{
|
||||
for( i = 0; i < ArrayCount(HitEffects); i++ )
|
||||
{
|
||||
if( HitEffects[i] == none )
|
||||
continue;
|
||||
|
||||
AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( class'GameInfo'.static.UseLowGore() )
|
||||
HitFX[SimHitFxTicker].bSever = false;
|
||||
|
||||
if( HitFX[SimHitFxTicker].bSever )
|
||||
{
|
||||
GibPerterbation = HitFX[SimHitFxTicker].damtype.default.GibPerterbation;
|
||||
|
||||
switch( HitFX[SimHitFxTicker].bone )
|
||||
{
|
||||
case 'obliterate':
|
||||
break;
|
||||
|
||||
case LeftThighBone:
|
||||
if( !bLeftLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bLeftLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RightThighBone:
|
||||
if( !bRightLegGibbed )
|
||||
{
|
||||
SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
|
||||
bRightLegGibbed=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LeftFArmBone:
|
||||
break;
|
||||
|
||||
case RightFArmBone:
|
||||
break;
|
||||
|
||||
case 'head':
|
||||
if( !bHeadGibbed )
|
||||
{
|
||||
if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
|
||||
}
|
||||
else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
|
||||
{
|
||||
DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
|
||||
}
|
||||
|
||||
bHeadGibbed=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
|
||||
HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
|
||||
HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
|
||||
HideBone(HitFX[SimHitFxTicker].bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.siren_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_diffuse');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.siren_hair');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.siren_hair_fb');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
screamTimings(0)=0.420000
|
||||
screamTimings(1)=0.510000
|
||||
screamTimings(2)=0.590000
|
||||
screamTimings(3)=0.670000
|
||||
screamTimings(4)=0.760000
|
||||
screamTimings(5)=0.840000
|
||||
stunLoopStart=0.200000
|
||||
stunLoopEnd=0.820000
|
||||
idleInsertFrame=0.920000
|
||||
EventClasses(0)="NicePack.NiceZombieSiren"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Talk'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Jump'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegSiren'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadSiren'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Death'
|
||||
ControllerClass=Class'NicePack.NiceZombieSirenController'
|
||||
AmbientSound=Sound'KF_BaseSiren.Siren_IdleLoop'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.Siren_Freak'
|
||||
Skins(0)=FinalBlend'KF_Specimens_Trip_T.siren_hair_fb'
|
||||
Skins(1)=Combiner'KF_Specimens_Trip_T.siren_cmb'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,97 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSirenBase extends NiceMonster
|
||||
abstract;
|
||||
var () int ScreamRadius; // AOE for scream attack.
|
||||
var () class <DamageType> ScreamDamageType;
|
||||
var () int ScreamForce;
|
||||
var(Shake) rotator RotMag; // how far to rot view
|
||||
var(Shake) float RotRate; // how fast to rot view
|
||||
var(Shake) vector OffsetMag; // max view offset vertically
|
||||
var(Shake) float OffsetRate; // how fast to offset view vertically
|
||||
var(Shake) float ShakeTime; // how long to shake for per scream
|
||||
var(Shake) float ShakeFadeTime; // how long after starting to shake to start fading out
|
||||
var(Shake) float ShakeEffectScalar; // Overall scale for shake/blur effect
|
||||
var(Shake) float MinShakeEffectScale;// The minimum that the shake effect drops off over distance
|
||||
var(Shake) float ScreamBlurScale; // How much motion blur to give from screams
|
||||
var bool bAboutToDie;
|
||||
var float DeathTimer;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
ScreamRadius=700
ScreamDamageType=Class'KFMod.SirenScreamDamage'
ScreamForce=-150000
RotMag=(Pitch=150,Yaw=150,Roll=150)
RotRate=500.000000
OffsetMag=(Y=5.000000,Z=1.000000)
OffsetRate=500.000000
ShakeTime=2.000000
ShakeFadeTime=0.250000
ShakeEffectScalar=1.000000
MinShakeEffectScale=0.600000
ScreamBlurScale=0.850000
StunThreshold=3.000000
fuelRatio=0.650000
clientHeadshotScale=1.200000
niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType'
MeleeAnims(0)="Siren_Bite"
MeleeAnims(1)="Siren_Bite2"
MeleeAnims(2)="Siren_Bite"
HitAnims(0)="HitReactionF"
HitAnims(1)="HitReactionF"
HitAnims(2)="HitReactionF"
ZapThreshold=0.500000
ZappedDamageMod=1.500000
ZombieFlag=1
MeleeDamage=13
damageForce=5000
KFRagdollName="Siren_Trip"
ScreamDamage=10
CrispUpThreshhold=7
bCanDistanceAttackDoors=True
bUseExtendedCollision=True
ColOffset=(Z=48.000000)
ColRadius=25.000000
ColHeight=5.000000
ExtCollAttachBoneName="Collision_Attach"
SeveredLegAttachScale=0.700000
PlayerCountHealthScale=0.100000
OnlineHeadshotOffset=(X=6.000000,Z=41.000000)
OnlineHeadshotScale=1.200000
HeadHealth=200.000000
PlayerNumHeadHealthScale=0.050000
MotionDetectorThreat=2.000000
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
ScoringValue=25
SoundGroupClass=Class'KFMod.KFFemaleZombieSounds'
IdleHeavyAnim="Siren_Idle"
IdleRifleAnim="Siren_Idle"
MeleeRange=45.000000
GroundSpeed=100.000000
WaterSpeed=80.000000
HealthMax=240.000000
Health=240
HeadHeight=1.000000
HeadScale=1.000000
MenuName="Nice Siren"
MovementAnims(0)="Siren_Walk"
MovementAnims(1)="Siren_Walk"
MovementAnims(2)="Siren_Walk"
MovementAnims(3)="Siren_Walk"
WalkAnims(0)="Siren_Walk"
WalkAnims(1)="Siren_Walk"
WalkAnims(2)="Siren_Walk"
WalkAnims(3)="Siren_Walk"
IdleCrouchAnim="Siren_Idle"
IdleWeaponAnim="Siren_Idle"
IdleRestAnim="Siren_Idle"
DrawScale=1.050000
PrePivot=(Z=3.000000)
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieSirenBase extends NiceMonster
|
||||
abstract;
|
||||
var () int ScreamRadius; // AOE for scream attack.
|
||||
var () class <DamageType> ScreamDamageType;
|
||||
var () int ScreamForce;
|
||||
var(Shake) rotator RotMag; // how far to rot view
|
||||
var(Shake) float RotRate; // how fast to rot view
|
||||
var(Shake) vector OffsetMag; // max view offset vertically
|
||||
var(Shake) float OffsetRate; // how fast to offset view vertically
|
||||
var(Shake) float ShakeTime; // how long to shake for per scream
|
||||
var(Shake) float ShakeFadeTime; // how long after starting to shake to start fading out
|
||||
var(Shake) float ShakeEffectScalar; // Overall scale for shake/blur effect
|
||||
var(Shake) float MinShakeEffectScale;// The minimum that the shake effect drops off over distance
|
||||
var(Shake) float ScreamBlurScale; // How much motion blur to give from screams
|
||||
var bool bAboutToDie;
|
||||
var float DeathTimer;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
ScreamRadius=700
|
||||
ScreamDamageType=Class'KFMod.SirenScreamDamage'
|
||||
ScreamForce=-150000
|
||||
RotMag=(Pitch=150,Yaw=150,Roll=150)
|
||||
RotRate=500.000000
|
||||
OffsetMag=(Y=5.000000,Z=1.000000)
|
||||
OffsetRate=500.000000
|
||||
ShakeTime=2.000000
|
||||
ShakeFadeTime=0.250000
|
||||
ShakeEffectScalar=1.000000
|
||||
MinShakeEffectScale=0.600000
|
||||
ScreamBlurScale=0.850000
|
||||
StunThreshold=3.000000
|
||||
fuelRatio=0.650000
|
||||
clientHeadshotScale=1.200000
|
||||
niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType'
|
||||
MeleeAnims(0)="Siren_Bite"
|
||||
MeleeAnims(1)="Siren_Bite2"
|
||||
MeleeAnims(2)="Siren_Bite"
|
||||
HitAnims(0)="HitReactionF"
|
||||
HitAnims(1)="HitReactionF"
|
||||
HitAnims(2)="HitReactionF"
|
||||
ZapThreshold=0.500000
|
||||
ZappedDamageMod=1.500000
|
||||
ZombieFlag=1
|
||||
MeleeDamage=13
|
||||
damageForce=5000
|
||||
KFRagdollName="Siren_Trip"
|
||||
ScreamDamage=10
|
||||
CrispUpThreshhold=7
|
||||
bCanDistanceAttackDoors=True
|
||||
bUseExtendedCollision=True
|
||||
ColOffset=(Z=48.000000)
|
||||
ColRadius=25.000000
|
||||
ColHeight=5.000000
|
||||
ExtCollAttachBoneName="Collision_Attach"
|
||||
SeveredLegAttachScale=0.700000
|
||||
PlayerCountHealthScale=0.100000
|
||||
OnlineHeadshotOffset=(X=6.000000,Z=41.000000)
|
||||
OnlineHeadshotScale=1.200000
|
||||
HeadHealth=200.000000
|
||||
PlayerNumHeadHealthScale=0.050000
|
||||
MotionDetectorThreat=2.000000
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.siren.Siren_Challenge'
|
||||
ScoringValue=25
|
||||
SoundGroupClass=Class'KFMod.KFFemaleZombieSounds'
|
||||
IdleHeavyAnim="Siren_Idle"
|
||||
IdleRifleAnim="Siren_Idle"
|
||||
MeleeRange=45.000000
|
||||
GroundSpeed=100.000000
|
||||
WaterSpeed=80.000000
|
||||
HealthMax=240.000000
|
||||
Health=240
|
||||
HeadHeight=1.000000
|
||||
HeadScale=1.000000
|
||||
MenuName="Nice Siren"
|
||||
MovementAnims(0)="Siren_Walk"
|
||||
MovementAnims(1)="Siren_Walk"
|
||||
MovementAnims(2)="Siren_Walk"
|
||||
MovementAnims(3)="Siren_Walk"
|
||||
WalkAnims(0)="Siren_Walk"
|
||||
WalkAnims(1)="Siren_Walk"
|
||||
WalkAnims(2)="Siren_Walk"
|
||||
WalkAnims(3)="Siren_Walk"
|
||||
IdleCrouchAnim="Siren_Idle"
|
||||
IdleWeaponAnim="Siren_Idle"
|
||||
IdleRestAnim="Siren_Idle"
|
||||
DrawScale=1.050000
|
||||
PrePivot=(Z=3.000000)
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,24 @@
|
|||
class NiceZombieSirenController extends NiceMonsterController;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
{
// 25% chance of first player to see this Siren saying something
if ( !KFGameType(Level.Game).bDidSpottedSirenMessage && FRand() < 0.25 )
{
PlayerController(SeenPlayer.Controller).Speech('AUTO', 15, "");
KFGameType(Level.Game).bDidSpottedSirenMessage = true;
}
|
||||
bDoneSpottedCheck = true;
}
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceZombieSirenController extends NiceMonsterController;
|
||||
var bool bDoneSpottedCheck;
|
||||
state ZombieHunt
|
||||
{
|
||||
event SeePlayer(Pawn SeenPlayer)
|
||||
{
|
||||
if ( !bDoneSpottedCheck && PlayerController(SeenPlayer.Controller) != none )
|
||||
{
|
||||
// 25% chance of first player to see this Siren saying something
|
||||
if ( !KFGameType(Level.Game).bDidSpottedSirenMessage && FRand() < 0.25 )
|
||||
{
|
||||
PlayerController(SeenPlayer.Controller).Speech('AUTO', 15, "");
|
||||
KFGameType(Level.Game).bDidSpottedSirenMessage = true;
|
||||
}
|
||||
|
||||
bDoneSpottedCheck = true;
|
||||
}
|
||||
|
||||
super.SeePlayer(SeenPlayer);
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,147 +1,297 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieStalker extends NiceZombieStalkerBase;
|
||||
#exec OBJ LOAD FILE=KFX.utx
|
||||
#exec OBJ LOAD FILE=KF_BaseStalker.uax
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
CloakStalker();
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
local PlayerController PC;
|
||||
super.PostNetBeginPlay();
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
{
PC = Level.GetLocalPlayerController();
if( PC != none && PC.Pawn != none )
{
LocalKFHumanPawn = KFHumanPawn(PC.Pawn);
}
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if ( NewAction == 'Claw' || NewAction == MeleeAnims[0] || NewAction == MeleeAnims[1] || NewAction == MeleeAnims[2] )
|
||||
{
UncloakStalker();
|
||||
}
|
||||
super.SetAnimAction(NewAction);
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
Super.Tick(DeltaTime);
|
||||
if( Level.NetMode==NM_DedicatedServer )
Return; // Servers aren't intrested in this info.
|
||||
if( bZapped )
|
||||
{
// Make sure we check if we need to be cloaked as soon as the zap wears off
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
if( LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() &&
VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0 ) // 640000 = 800 Units
{
bSpotted = True;
}
else
{
bSpotted = false;
}
|
||||
if ( !bSpotted && !bCloaked && Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' )
{
UncloakStalker();
}
else if ( Level.TimeSeconds - LastUncloakTime > 1.2 )
{
// if we're uberbrite, turn down the light
if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' )
{
bUnlit = false;
CloakStalker();
}
else if ( Skins[0] != Shader'KF_Specimens_Trip_T.stalker_invisible' )
{
CloakStalker();
}
}
|
||||
}
|
||||
}
|
||||
// Cloak Functions ( called from animation notifies to save Gibby trouble ;) )
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if ( bSpotted )
|
||||
{
if( Level.NetMode == NM_DedicatedServer )
return;
|
||||
Skins[0] = Finalblend'KFX.StalkerGlow';
Skins[1] = Finalblend'KFX.StalkerGlow';
bUnlit = true;
return;
|
||||
}
|
||||
if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
|
||||
{
Visibility = 1;
bCloaked = true;
|
||||
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
Skins[0] = Shader'KF_Specimens_Trip_T.stalker_invisible';
Skins[1] = Shader'KF_Specimens_Trip_T.stalker_invisible';
|
||||
// Invisible - no shadow
if(PlayerShadow != none)
PlayerShadow.bShadowActive = false;
if(RealTimeShadow != none)
RealTimeShadow.Destroy();
|
||||
// Remove/disallow projectors on invisible people
Projectors.Remove(0, Projectors.Length);
bAcceptsProjectors = false;
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
LastUncloakTime = Level.TimeSeconds;
|
||||
Visibility = default.Visibility;
bCloaked = false;
bUnlit = false;
|
||||
// 25% chance of our Enemy saying something about us being invisible
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
PlayerController(Controller.Enemy.Controller)!=none )
{
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
}
if( Level.NetMode == NM_DedicatedServer )
Return;
|
||||
if ( Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' )
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
}
|
||||
}
|
||||
}
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
super.SetZappedBehavior();
|
||||
bUnlit = false;
|
||||
// Handle setting the zed to uncloaked so the zapped overlay works properly
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
if (PlayerShadow != none)
PlayerShadow.bShadowActive = true;
|
||||
bAcceptsProjectors = true;
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
|
||||
}
|
||||
}
|
||||
// Turn off the zapped behavior
|
||||
simulated function UnSetZappedBehavior()
|
||||
{
|
||||
super.UnSetZappedBehavior();
|
||||
// Handle getting the zed back cloaked if need be
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
NextCheckTime = Level.TimeSeconds;
SetOverlayMaterial(none, 0.0f, true);
|
||||
}
|
||||
}
|
||||
// Overridden because we need to handle the overlays differently for zombies that can cloak
|
||||
function SetZapped(float ZapAmount, Pawn Instigator)
|
||||
{
|
||||
LastZapTime = Level.TimeSeconds;
|
||||
if( bZapped )
|
||||
{
TotalZap = ZapThreshold;
RemainingZap = ZapDuration;
|
||||
}
|
||||
else
|
||||
{
TotalZap += ZapAmount;
|
||||
if( TotalZap >= ZapThreshold )
{
RemainingZap = ZapDuration;
bZapped = true;
}
|
||||
}
|
||||
ZappedBy = Instigator;
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super.RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super.PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
}
|
||||
}
|
||||
// Give her the ability to spring.
|
||||
function bool DoJump( bool bUpdating )
|
||||
{
|
||||
if ( !bIsCrouched && !bWantsToCrouch && ((Physics == PHYS_Walking) || (Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) )
|
||||
{
if ( Role == ROLE_Authority )
{
if (Level.Game != none)
MakeNoise(1.0);
if ( bCountJumps && (Inventory != none) )
Inventory.OwnerEvent('Jumped');
}
if ( Physics == PHYS_Spider )
Velocity = JumpZ * Floor;
else if ( Physics == PHYS_Ladder )
Velocity.Z = 0;
else if ( bIsWalking )
{
Velocity.Z = Default.JumpZ;
Velocity.X = (Default.JumpZ * 0.6);
}
else
{
Velocity.Z = JumpZ;
Velocity.X = (JumpZ * 0.6);
}
if ( (Base != none) && !Base.bWorldGeometry )
{
Velocity.Z += Base.Velocity.Z;
Velocity.X += Base.Velocity.X;
}
SetPhysics(PHYS_Falling);
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_invisible');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
defaultproperties
|
||||
{
stunLoopStart=0.250000
stunLoopEnd=0.890000
idleInsertFrame=0.950000
EventClasses(0)="NicePack.NiceZombieStalker"
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Talk'
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_HitPlayer'
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Jump'
DetachedArmClass=Class'KFChar.SeveredArmStalker'
DetachedLegClass=Class'KFChar.SeveredLegStalker'
DetachedHeadClass=Class'KFChar.SeveredHeadStalker'
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Pain'
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Death'
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
AmbientSound=Sound'KF_BaseStalker.Stalker_IdleLoop'
Mesh=SkeletalMesh'KF_Freaks_Trip.Stalker_Freak'
Skins(0)=Shader'KF_Specimens_Trip_T.stalker_invisible'
Skins(1)=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieStalker extends NiceZombieStalkerBase;
|
||||
#exec OBJ LOAD FILE=KFX.utx
|
||||
#exec OBJ LOAD FILE=KF_BaseStalker.uax
|
||||
//----------------------------------------------------------------------------
|
||||
// NOTE: All Variables are declared in the base class to eliminate hitching
|
||||
//----------------------------------------------------------------------------
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
CloakStalker();
|
||||
super.PostBeginPlay();
|
||||
}
|
||||
simulated function PostNetBeginPlay()
|
||||
{
|
||||
local PlayerController PC;
|
||||
super.PostNetBeginPlay();
|
||||
if( Level.NetMode!=NM_DedicatedServer )
|
||||
{
|
||||
PC = Level.GetLocalPlayerController();
|
||||
if( PC != none && PC.Pawn != none )
|
||||
{
|
||||
LocalKFHumanPawn = KFHumanPawn(PC.Pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
simulated event SetAnimAction(name NewAction)
|
||||
{
|
||||
if ( NewAction == 'Claw' || NewAction == MeleeAnims[0] || NewAction == MeleeAnims[1] || NewAction == MeleeAnims[2] )
|
||||
{
|
||||
UncloakStalker();
|
||||
}
|
||||
super.SetAnimAction(NewAction);
|
||||
}
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
Super.Tick(DeltaTime);
|
||||
if( Level.NetMode==NM_DedicatedServer )
|
||||
Return; // Servers aren't intrested in this info.
|
||||
if( bZapped )
|
||||
{
|
||||
// Make sure we check if we need to be cloaked as soon as the zap wears off
|
||||
NextCheckTime = Level.TimeSeconds;
|
||||
}
|
||||
else if( Level.TimeSeconds > NextCheckTime && Health > 0 )
|
||||
{
|
||||
NextCheckTime = Level.TimeSeconds + 0.5;
|
||||
|
||||
if( LocalKFHumanPawn != none && LocalKFHumanPawn.Health > 0 && LocalKFHumanPawn.ShowStalkers() &&
|
||||
VSizeSquared(Location - LocalKFHumanPawn.Location) < LocalKFHumanPawn.GetStalkerViewDistanceMulti() * 640000.0 ) // 640000 = 800 Units
|
||||
{
|
||||
bSpotted = True;
|
||||
}
|
||||
else
|
||||
{
|
||||
bSpotted = false;
|
||||
}
|
||||
|
||||
if ( !bSpotted && !bCloaked && Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' )
|
||||
{
|
||||
UncloakStalker();
|
||||
}
|
||||
else if ( Level.TimeSeconds - LastUncloakTime > 1.2 )
|
||||
{
|
||||
// if we're uberbrite, turn down the light
|
||||
if( bSpotted && Skins[0] != Finalblend'KFX.StalkerGlow' )
|
||||
{
|
||||
bUnlit = false;
|
||||
CloakStalker();
|
||||
}
|
||||
else if ( Skins[0] != Shader'KF_Specimens_Trip_T.stalker_invisible' )
|
||||
{
|
||||
CloakStalker();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cloak Functions ( called from animation notifies to save Gibby trouble ;) )
|
||||
simulated function CloakStalker()
|
||||
{
|
||||
// No cloaking if zapped
|
||||
if( bZapped )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( bSpotted )
|
||||
{
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
return;
|
||||
|
||||
Skins[0] = Finalblend'KFX.StalkerGlow';
|
||||
Skins[1] = Finalblend'KFX.StalkerGlow';
|
||||
bUnlit = true;
|
||||
return;
|
||||
}
|
||||
if ( !bDecapitated && !bCrispified ) // No head, no cloak, honey. updated : Being charred means no cloak either :D
|
||||
{
|
||||
Visibility = 1;
|
||||
bCloaked = true;
|
||||
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
Return;
|
||||
|
||||
Skins[0] = Shader'KF_Specimens_Trip_T.stalker_invisible';
|
||||
Skins[1] = Shader'KF_Specimens_Trip_T.stalker_invisible';
|
||||
|
||||
// Invisible - no shadow
|
||||
if(PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = false;
|
||||
if(RealTimeShadow != none)
|
||||
RealTimeShadow.Destroy();
|
||||
|
||||
// Remove/disallow projectors on invisible people
|
||||
Projectors.Remove(0, Projectors.Length);
|
||||
bAcceptsProjectors = false;
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
simulated function UnCloakStalker()
|
||||
{
|
||||
if( bZapped )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( !bCrispified )
|
||||
{
|
||||
LastUncloakTime = Level.TimeSeconds;
|
||||
|
||||
Visibility = default.Visibility;
|
||||
bCloaked = false;
|
||||
bUnlit = false;
|
||||
|
||||
// 25% chance of our Enemy saying something about us being invisible
|
||||
if( Level.NetMode!=NM_Client && !KFGameType(Level.Game).bDidStalkerInvisibleMessage && FRand()<0.25 && Controller.Enemy!=none &&
|
||||
PlayerController(Controller.Enemy.Controller)!=none )
|
||||
{
|
||||
PlayerController(Controller.Enemy.Controller).Speech('AUTO', 17, "");
|
||||
KFGameType(Level.Game).bDidStalkerInvisibleMessage = true;
|
||||
}
|
||||
if( Level.NetMode == NM_DedicatedServer )
|
||||
Return;
|
||||
|
||||
if ( Skins[0] != Combiner'KF_Specimens_Trip_T.stalker_cmb' )
|
||||
{
|
||||
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
|
||||
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
|
||||
if (PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = true;
|
||||
|
||||
bAcceptsProjectors = true;
|
||||
|
||||
SetOverlayMaterial(Material'KFX.FBDecloakShader', 0.25, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the zed to the zapped behavior
|
||||
simulated function SetZappedBehavior()
|
||||
{
|
||||
super.SetZappedBehavior();
|
||||
bUnlit = false;
|
||||
// Handle setting the zed to uncloaked so the zapped overlay works properly
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
|
||||
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
|
||||
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
|
||||
if (PlayerShadow != none)
|
||||
PlayerShadow.bShadowActive = true;
|
||||
|
||||
bAcceptsProjectors = true;
|
||||
SetOverlayMaterial(Material'KFZED_FX_T.Energy.ZED_overlay_Hit_Shdr', 999, true);
|
||||
}
|
||||
}
|
||||
// Turn off the zapped behavior
|
||||
simulated function UnSetZappedBehavior()
|
||||
{
|
||||
super.UnSetZappedBehavior();
|
||||
// Handle getting the zed back cloaked if need be
|
||||
if( Level.Netmode != NM_DedicatedServer )
|
||||
{
|
||||
NextCheckTime = Level.TimeSeconds;
|
||||
SetOverlayMaterial(none, 0.0f, true);
|
||||
}
|
||||
}
|
||||
// Overridden because we need to handle the overlays differently for zombies that can cloak
|
||||
function SetZapped(float ZapAmount, Pawn Instigator)
|
||||
{
|
||||
LastZapTime = Level.TimeSeconds;
|
||||
if( bZapped )
|
||||
{
|
||||
TotalZap = ZapThreshold;
|
||||
RemainingZap = ZapDuration;
|
||||
}
|
||||
else
|
||||
{
|
||||
TotalZap += ZapAmount;
|
||||
|
||||
if( TotalZap >= ZapThreshold )
|
||||
{
|
||||
RemainingZap = ZapDuration;
|
||||
bZapped = true;
|
||||
}
|
||||
}
|
||||
ZappedBy = Instigator;
|
||||
}
|
||||
function RemoveHead()
|
||||
{
|
||||
Super.RemoveHead();
|
||||
if (!bCrispified)
|
||||
{
|
||||
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
|
||||
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
}
|
||||
}
|
||||
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
||||
{
|
||||
Super.PlayDying(DamageType,HitLoc);
|
||||
if(bUnlit)
|
||||
bUnlit=!bUnlit;
|
||||
LocalKFHumanPawn = none;
|
||||
if (!bCrispified)
|
||||
{
|
||||
Skins[1] = FinalBlend'KF_Specimens_Trip_T.stalker_fb';
|
||||
Skins[0] = Combiner'KF_Specimens_Trip_T.stalker_cmb';
|
||||
}
|
||||
}
|
||||
// Give her the ability to spring.
|
||||
function bool DoJump( bool bUpdating )
|
||||
{
|
||||
if ( !bIsCrouched && !bWantsToCrouch && ((Physics == PHYS_Walking) || (Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) )
|
||||
{
|
||||
if ( Role == ROLE_Authority )
|
||||
{
|
||||
if (Level.Game != none)
|
||||
MakeNoise(1.0);
|
||||
if ( bCountJumps && (Inventory != none) )
|
||||
Inventory.OwnerEvent('Jumped');
|
||||
}
|
||||
if ( Physics == PHYS_Spider )
|
||||
Velocity = JumpZ * Floor;
|
||||
else if ( Physics == PHYS_Ladder )
|
||||
Velocity.Z = 0;
|
||||
else if ( bIsWalking )
|
||||
{
|
||||
Velocity.Z = Default.JumpZ;
|
||||
Velocity.X = (Default.JumpZ * 0.6);
|
||||
}
|
||||
else
|
||||
{
|
||||
Velocity.Z = JumpZ;
|
||||
Velocity.X = (JumpZ * 0.6);
|
||||
}
|
||||
if ( (Base != none) && !Base.bWorldGeometry )
|
||||
{
|
||||
Velocity.Z += Base.Velocity.Z;
|
||||
Velocity.X += Base.Velocity.X;
|
||||
}
|
||||
SetPhysics(PHYS_Falling);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static simulated function PreCacheMaterials(LevelInfo myLevel)
|
||||
{//should be derived and used.
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_cmb');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.stalker_env_cmb');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_diff');
|
||||
myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.stalker_spec');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_invisible');
|
||||
myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.StalkerCloakOpacity_cmb');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.StalkerCloakEnv_rot');
|
||||
myLevel.AddPrecacheMaterial(Material'KF_Specimens_Trip_T.stalker_opacity_osc');
|
||||
myLevel.AddPrecacheMaterial(Material'KFCharacters.StalkerSkin');
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
stunLoopStart=0.250000
|
||||
stunLoopEnd=0.890000
|
||||
idleInsertFrame=0.950000
|
||||
EventClasses(0)="NicePack.NiceZombieStalker"
|
||||
MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Talk'
|
||||
MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_HitPlayer'
|
||||
JumpSound=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Jump'
|
||||
DetachedArmClass=Class'KFChar.SeveredArmStalker'
|
||||
DetachedLegClass=Class'KFChar.SeveredLegStalker'
|
||||
DetachedHeadClass=Class'KFChar.SeveredHeadStalker'
|
||||
HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Pain'
|
||||
DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Death'
|
||||
ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
|
||||
ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
|
||||
ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
|
||||
ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Stalker.Stalker_Challenge'
|
||||
AmbientSound=Sound'KF_BaseStalker.Stalker_IdleLoop'
|
||||
Mesh=SkeletalMesh'KF_Freaks_Trip.Stalker_Freak'
|
||||
Skins(0)=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
Skins(1)=Shader'KF_Specimens_Trip_T.stalker_invisible'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,57 @@
|
|||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieStalkerBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
#exec OBJ LOAD FILE=KFX.utx
|
||||
#exec OBJ LOAD FILE=KF_BaseStalker.uax
|
||||
var float NextCheckTime;
|
||||
var KFHumanPawn LocalKFHumanPawn;
|
||||
var float LastUncloakTime;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
fuelRatio=0.850000
clientHeadshotScale=1.200000
niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType'
MeleeAnims(0)="StalkerSpinAttack"
MeleeAnims(1)="StalkerAttack1"
MeleeAnims(2)="JumpAttack"
MeleeDamage=9
damageForce=5000
KFRagdollName="Stalker_Trip"
CrispUpThreshhold=10
PuntAnim="ClotPunt"
SeveredArmAttachScale=0.800000
SeveredLegAttachScale=0.700000
OnlineHeadshotOffset=(X=18.000000,Z=33.000000)
OnlineHeadshotScale=1.200000
MotionDetectorThreat=0.250000
ScoringValue=15
SoundGroupClass=Class'KFMod.KFFemaleZombieSounds'
IdleHeavyAnim="StalkerIdle"
IdleRifleAnim="StalkerIdle"
MeleeRange=30.000000
GroundSpeed=200.000000
WaterSpeed=180.000000
JumpZ=350.000000
Health=100
HeadHeight=2.500000
MenuName="Nice Stalker"
MovementAnims(0)="ZombieRun"
MovementAnims(1)="ZombieRun"
MovementAnims(2)="ZombieRun"
MovementAnims(3)="ZombieRun"
WalkAnims(0)="ZombieRun"
WalkAnims(1)="ZombieRun"
WalkAnims(2)="ZombieRun"
WalkAnims(3)="ZombieRun"
IdleCrouchAnim="StalkerIdle"
IdleWeaponAnim="StalkerIdle"
IdleRestAnim="StalkerIdle"
DrawScale=1.100000
PrePivot=(Z=5.000000)
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
// Zombie Monster for KF Invasion gametype
|
||||
class NiceZombieStalkerBase extends NiceMonster
|
||||
abstract;
|
||||
#exec OBJ LOAD FILE=
|
||||
#exec OBJ LOAD FILE=KFX.utx
|
||||
#exec OBJ LOAD FILE=KF_BaseStalker.uax
|
||||
var float NextCheckTime;
|
||||
var KFHumanPawn LocalKFHumanPawn;
|
||||
var float LastUncloakTime;
|
||||
//-------------------------------------------------------------------------------
|
||||
// NOTE: All Code resides in the child class(this class was only created to
|
||||
// eliminate hitching caused by loading default properties during play)
|
||||
//-------------------------------------------------------------------------------
|
||||
defaultproperties
|
||||
{
|
||||
fuelRatio=0.850000
|
||||
clientHeadshotScale=1.200000
|
||||
niceZombieDamType=Class'NicePack.NiceZedSlashingDamageType'
|
||||
MeleeAnims(0)="StalkerSpinAttack"
|
||||
MeleeAnims(1)="StalkerAttack1"
|
||||
MeleeAnims(2)="JumpAttack"
|
||||
MeleeDamage=9
|
||||
damageForce=5000
|
||||
KFRagdollName="Stalker_Trip"
|
||||
CrispUpThreshhold=10
|
||||
PuntAnim="ClotPunt"
|
||||
SeveredArmAttachScale=0.800000
|
||||
SeveredLegAttachScale=0.700000
|
||||
OnlineHeadshotOffset=(X=18.000000,Z=33.000000)
|
||||
OnlineHeadshotScale=1.200000
|
||||
MotionDetectorThreat=0.250000
|
||||
ScoringValue=15
|
||||
SoundGroupClass=Class'KFMod.KFFemaleZombieSounds'
|
||||
IdleHeavyAnim="StalkerIdle"
|
||||
IdleRifleAnim="StalkerIdle"
|
||||
MeleeRange=30.000000
|
||||
GroundSpeed=200.000000
|
||||
WaterSpeed=180.000000
|
||||
JumpZ=350.000000
|
||||
Health=100
|
||||
HeadHeight=2.500000
|
||||
MenuName="Nice Stalker"
|
||||
MovementAnims(0)="ZombieRun"
|
||||
MovementAnims(1)="ZombieRun"
|
||||
MovementAnims(2)="ZombieRun"
|
||||
MovementAnims(3)="ZombieRun"
|
||||
WalkAnims(0)="ZombieRun"
|
||||
WalkAnims(1)="ZombieRun"
|
||||
WalkAnims(2)="ZombieRun"
|
||||
WalkAnims(3)="ZombieRun"
|
||||
IdleCrouchAnim="StalkerIdle"
|
||||
IdleWeaponAnim="StalkerIdle"
|
||||
IdleRestAnim="StalkerIdle"
|
||||
DrawScale=1.100000
|
||||
PrePivot=(Z=5.000000)
|
||||
RotationRate=(Yaw=45000,Roll=0)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,11 @@
|
|||
class NiceAvoidMarker extends AvoidMarker;
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceZombieFleshpound niceFP;
|
||||
niceFP = NiceZombieFleshpound(P);
|
||||
if(niceFP != none && niceFP.IsInState('RageCharging'))
return false;
|
||||
return super.RelevantTo(P);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceAvoidMarker extends AvoidMarker;
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceZombieFleshpound niceFP;
|
||||
niceFP = NiceZombieFleshpound(P);
|
||||
if(niceFP != none && niceFP.IsInState('RageCharging'))
|
||||
return false;
|
||||
return super.RelevantTo(P);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
class NiceAvoidMarkerExplosive extends NiceAvoidMarker;
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(P);
|
||||
if(niceZed != none && niceZed.default.Health >= 1000 && NiceZombieFleshpound(P) == none)
return false;
|
||||
return super.RelevantTo(P);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceAvoidMarkerExplosive extends NiceAvoidMarker;
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(P);
|
||||
if(niceZed != none && niceZed.default.Health >= 1000 && NiceZombieFleshpound(P) == none)
|
||||
return false;
|
||||
return super.RelevantTo(P);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,38 @@
|
|||
class NiceAvoidMarkerFP extends NiceAvoidMarker;
|
||||
var NiceZombieFleshpound niceFP;
|
||||
state BigMeanAndScary
|
||||
{
|
||||
Begin:
|
||||
StartleBots();
|
||||
Sleep(1.0);
|
||||
GoTo('Begin');
|
||||
}
|
||||
function InitFor(NiceMonster V){
|
||||
if(V != none){
niceFP = NiceZombieFleshpound(V);
SetCollisionSize(niceFP.CollisionRadius * 3, niceFP.CollisionHeight + CollisionHeight);
SetBase(niceFP);
GoToState('BigMeanAndScary');
|
||||
}
|
||||
}
|
||||
function Touch( actor Other ){
|
||||
if((Pawn(Other) != none) && KFMonsterController(Pawn(Other).Controller) != none && RelevantTo(Pawn(Other)))
KFMonsterController(Pawn(Other).Controller).AvoidThisMonster(niceFP);
|
||||
}
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(P);
|
||||
if(niceZed != none && niceZed.default.Health >= 1500)
return false;
|
||||
return (niceFP != none && VSizeSquared(niceFP.Velocity) >= 75 && Super.RelevantTo(P) && niceFP.Velocity dot (P.Location - niceFP.Location) > 0 );
|
||||
}
|
||||
function StartleBots(){
|
||||
local KFMonster P;
|
||||
if(niceFP != none)
ForEach CollidingActors(class'KFMonster', P, CollisionRadius)
if(RelevantTo(P))
KFMonsterController(P.Controller).AvoidThisMonster(niceFP);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceAvoidMarkerFP extends NiceAvoidMarker;
|
||||
var NiceZombieFleshpound niceFP;
|
||||
state BigMeanAndScary
|
||||
{
|
||||
Begin:
|
||||
StartleBots();
|
||||
Sleep(1.0);
|
||||
GoTo('Begin');
|
||||
}
|
||||
function InitFor(NiceMonster V){
|
||||
if(V != none){
|
||||
niceFP = NiceZombieFleshpound(V);
|
||||
SetCollisionSize(niceFP.CollisionRadius * 3, niceFP.CollisionHeight + CollisionHeight);
|
||||
SetBase(niceFP);
|
||||
GoToState('BigMeanAndScary');
|
||||
}
|
||||
}
|
||||
function Touch( actor Other ){
|
||||
if((Pawn(Other) != none) && KFMonsterController(Pawn(Other).Controller) != none && RelevantTo(Pawn(Other)))
|
||||
KFMonsterController(Pawn(Other).Controller).AvoidThisMonster(niceFP);
|
||||
}
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(P);
|
||||
if(niceZed != none && niceZed.default.Health >= 1500)
|
||||
return false;
|
||||
return (niceFP != none && VSizeSquared(niceFP.Velocity) >= 75 && Super.RelevantTo(P) && niceFP.Velocity dot (P.Location - niceFP.Location) > 0 );
|
||||
}
|
||||
function StartleBots(){
|
||||
local KFMonster P;
|
||||
if(niceFP != none)
|
||||
ForEach CollidingActors(class'KFMonster', P, CollisionRadius)
|
||||
if(RelevantTo(P))
|
||||
KFMonsterController(P.Controller).AvoidThisMonster(niceFP);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
class NiceAvoidMarkerFlame extends NiceAvoidMarker;
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(P);
|
||||
if(niceZed != none && niceZed.bFireImmune)
return false;
|
||||
return super.RelevantTo(P);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
class NiceAvoidMarkerFlame extends NiceAvoidMarker;
|
||||
function bool RelevantTo(Pawn P){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(P);
|
||||
if(niceZed != none && niceZed.bFireImmune)
|
||||
return false;
|
||||
return super.RelevantTo(P);
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,225 +1,461 @@
|
|||
//==============================================================================
|
||||
// NicePack / NiceMonsterController
|
||||
//==============================================================================
|
||||
// New base class for zeds that makes it easier to implement various changes
|
||||
// and bug fixes.
|
||||
// Functionality:
|
||||
// - Removed threat assessment functionality in favor of vanilla's
|
||||
// distance-based behavior
|
||||
// - Doesn't support 'bNoAutoHuntEnemies' flag from 'KFMonster'
|
||||
//==============================================================================
|
||||
// 'Nice pack' source
|
||||
// Do whatever the fuck you want with it
|
||||
// Author: dkanus
|
||||
// E-mail: dkanus@gmail.com
|
||||
//==============================================================================
|
||||
class NiceMonsterController extends KFMonsterController;
|
||||
// Just reset threat assesment flag, since it's not used in NicePack
|
||||
function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
bUseThreatAssessment = true;
|
||||
}
|
||||
event bool NotifyBump(Actor other){
|
||||
local Pawn otherPawn;
|
||||
Disable('NotifyBump');
|
||||
otherPawn = Pawn(other);
|
||||
if(otherPawn == none || otherPawn.controller == none) return false;
|
||||
if(enemy == otherPawn) return false;
|
||||
if(SetEnemy(otherPawn)){
WhatToDoNext(4);
return false;
|
||||
}
|
||||
if(enemy == otherPawn) return false;
|
||||
if(!AdjustAround(otherPawn))
CancelCampFor(otherPawn.controller);
|
||||
return false;
|
||||
}
|
||||
state Startled{
|
||||
ignores EnemyNotVisible,SeePlayer,HearNoise;
|
||||
function Startle(Actor Feared){
goalString = "STARTLED!";
startleActor = feared;
BeginState();
|
||||
}
|
||||
function BeginState(){
if(startleActor == none){
GotoState('');
return;
}
pawn.acceleration = pawn.location - startleActor.location;
pawn.acceleration.Z = 0;
pawn.bIsWalking = false;
pawn.bWantsToCrouch = false;
if(pawn.acceleration == vect(0,0,0))
pawn.acceleration = VRand();
pawn.acceleration = pawn.accelRate * Normal(pawn.acceleration);
|
||||
}
|
||||
Begin:
|
||||
if( NiceHumanPawn(StartleActor) == none
|| KFGameType(Level.Game) == none
|| KFGameType(Level.Game).bZEDTimeActive ){
Sleep(0.5);
WhatToDoNext(11);
|
||||
}
|
||||
else{
Sleep(0.25);
Goto('Begin');
|
||||
}
|
||||
}
|
||||
function bool IsMonsterDecapitated(){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(self.pawn);
|
||||
if(niceZed == none) return false;
|
||||
return niceZed.bDecapitated || niceZed.HeadHealth <= 0;
|
||||
}
|
||||
function bool IsMonsterMad(){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(self.pawn);
|
||||
if(niceZed == none) return false;
|
||||
return niceZed.madnessCountDown > 0.0;
|
||||
}
|
||||
function bool FindNewEnemy(){
|
||||
local bool bSeeBest;
|
||||
local bool bDecapitated, bAttacksAnything;
|
||||
local float bestScore, newScore;
|
||||
local Pawn bestEnemy;
|
||||
local Controller ctrlIter;
|
||||
if(pawn == none) return false;
|
||||
bDecapitated = IsMonsterDecapitated();
|
||||
bAttacksAnything = bDecapitated || IsMonsterMad();
|
||||
for(ctrlIter = Level.controllerList;
ctrlIter != none;
ctrlIter = ctrlIter.nextController){
if(ctrlIter == none || ctrlIter.pawn == none) continue;
if(ctrlIter.pawn.health <= 0 || ctrlIter.pawn == self.pawn) continue;
if(ctrlIter.bPendingDelete || ctrlIter.pawn.bPendingDelete) continue;
// Shouldn't normally attack healthy zeds
if( !bAttacksAnything && NiceMonster(ctrlIter.pawn) != none
&& ctrlIter.pawn.health > 15) continue;
// Can only stand up to fleshpound if we're decapitated
if(NiceZombieFleshpound(ctrlIter.pawn) != none && !bDecapitated)
continue;
|
||||
// NicePack doesn't use threat assesment, so just find closest target
newScore = VSizeSquared(ctrlIter.pawn.Location - pawn.Location);
if(bestEnemy == none || newScore < bestScore){
bestEnemy = ctrlIter.pawn;
bestScore = newScore;
bSeeBest = CanSee(bestEnemy);
}
|
||||
}
|
||||
if(bestEnemy == enemy) return false;
|
||||
if(bestEnemy != none){
ChangeEnemy(bestEnemy, bSeeBest);
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function UpdatePathFindState(){
|
||||
if(pathFindState == 0){
initialPathGoal = FindRandomDest();
pathFindState = 1;
|
||||
}
|
||||
if(pathFindState == 1){
if(initialPathGoal == none)
pathFindState = 2;
else if(ActorReachable(initialPathGoal)){
MoveTarget = initialPathGoal;
pathFindState = 2;
return;
}
else if(FindBestPathToward(initialPathGoal, true, true))
return;
else
pathFindState = 2;
|
||||
}
|
||||
}
|
||||
function PickRandomDestination(){
|
||||
local bool bCloseToEnemy;
|
||||
local bool bCanTrackEnemy;
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(pawn);
|
||||
if(niceZed == none) return;
|
||||
if(enemy != none)
bCloseToEnemy = VSizeSquared(niceZed.Location - enemy.Location) < 40000;
|
||||
// Can we track our enemy?
|
||||
if(enemy != none && niceZed.headHealth > 0 && FRand() < 0.5){
bCanTrackEnemy = MoveTarget == enemy;
if(!ActorReachable(enemy))
bCanTrackEnemy = false;
if(niceZed.default.health < 500 && !bCloseToEnemy)
bCanTrackEnemy = false;
|
||||
}
|
||||
// Choose random location
|
||||
if(bCanTrackEnemy)
destination = enemy.location + VRand() * 50;
|
||||
else
destination = niceZed.location + VRand() * 200;
|
||||
}
|
||||
state ZombieHunt{
|
||||
function BeginState(){
local float zDif;
|
||||
if(pawn.collisionRadius > 27 || pawn.collisionHeight > 46){
zDif = Pawn.collisionHeight - 44;
Pawn.SetCollisionSize(24, 44);
Pawn.MoveSmooth(vect(0,0,-1) * zDif);
}
|
||||
}
|
||||
function EndState(){
local float zDif;
local bool bCollisionSizeChanged;
|
||||
bCollisionSizeChanged =
pawn.collisionRadius != pawn.default.collisionRadius;
bCollisionSizeChanged = bCollisionSizeChanged ||
pawn.collisionHeight != pawn.default.collisionHeight;
|
||||
if(pawn != none && bCollisionSizeChanged){
zDif = pawn.Default.collisionRadius - 44;
pawn.MoveSmooth(vect(0,0,1) * zDif);
pawn.SetCollisionSize( pawn.Default.collisionRadius,
pawn.Default.collisionHeight);
}
|
||||
}
|
||||
function Timer(){
if(pawn.Velocity == vect(0,0,0))
GotoState('ZombieRestFormation', 'Moving');
SetCombatTimer();
StopFiring();
|
||||
}
|
||||
function PickDestinationEnemyDied(){
|
||||
}
|
||||
function PickDestination(){
// Change behaviour in case we're 'BRAINS_Retarded'
if(KFM.intelligence == BRAINS_Retarded){
// Some of the TWI's code
if(FindFreshBody()) return;
if(enemy != none && !KFM.bCannibal && enemy.health <= 0){
enemy = none;
WhatToDoNext(23);
return;
}
UpdatePathFindState();
if(pawn.JumpZ > 0)
pawn.bCanJump = true;
// And just pick random location
PickRandomDestination();
return;
}
else
super.PickDestination();
|
||||
}
|
||||
}
|
||||
function NotifyTakeHit( Pawn InstigatedBy,
Vector HitLocation,
int damage,
class<DamageType> damageType,
Vector momentum){
|
||||
local KFMonster zed;
|
||||
local bool bZedCanVomit;
|
||||
if(class<DamTypeBlowerThrower>(damageType) == none || damage <= 0) return;
|
||||
foreach VisibleCollidingActors(class'KFMonster', zed, 1000, pawn.location){
bZedCanVomit = zed.IsA('NiceZombieBloatBase');
bZedCanVomit = bZedCanVomit || zed.IsA('NiceZombieSickBase');
if(bZedCanVomit && zed != pawn && KFHumanPawn(instigatedBy) != none){
if(KFMonster(pawn) != none)
SetEnemy(zed, true, KFMonster(pawn).HumanBileAggroChance);
return;
}
|
||||
}
|
||||
super.NotifyTakeHit(InstigatedBy,HitLocation, damage, damageType, momentum);
|
||||
}
|
||||
state ZombieCharge{
|
||||
function SeePlayer(Pawn seen){
if(KFM.intelligence == BRAINS_Human)
SetEnemy(Seen);
|
||||
}
|
||||
function DamageAttitudeTo(Pawn other, float damage){
if(KFM.intelligence >= BRAINS_Mammal && other!=none && SetEnemy(other))
SetEnemy(other);
|
||||
}
|
||||
function HearNoise(float loudness, Actor noiseMaker){
if(KFM.intelligence != BRAINS_Human) return;
if(noiseMaker == none && noiseMaker.Instigator == none) return;
|
||||
if(FastTrace(noiseMaker.location, pawn.location))
SetEnemy(noiseMaker.Instigator);
|
||||
}
|
||||
function bool StrafeFromDamage( float damage,
class<DamageType> damageType,
bool bFindDest){
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir){
return false;
|
||||
}
|
||||
Begin:
|
||||
if(pawn.physics == PHYS_Falling){
focus = enemy;
destination = enemy.location;
WaitForLanding();
|
||||
}
|
||||
if(enemy == none)
WhatToDoNext(16);
|
||||
WaitForAnim:
|
||||
while(KFM.bShotAnim)
Sleep(0.35);
|
||||
if(!FindBestPathToward(enemy, false, true))
GotoState('TacticalMove');
|
||||
Moving:
|
||||
if(KFM.intelligence == BRAINS_Retarded){
if( KFMonster(pawn).HeadHealth > 0 && moveTarget == enemy
&& FRand() < 0.5
&& ( KFMonster(pawn).default.Health >= 500
|| VSize(pawn.location - moveTarget.location) < 200)
)
MoveTo(moveTarget.location + VRand() * 50, none);
else
MoveTo(pawn.location + VRand() * 200, none);
|
||||
}
|
||||
else
MoveToward(moveTarget, FaceActor(1),, ShouldStrafeTo(moveTarget));
|
||||
WhatToDoNext(17);
|
||||
if (bSoaking)
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state DoorBashing{
|
||||
ignores EnemyNotVisible,SeeMonster;
|
||||
function Timer(){
Disable('NotifyBump');
|
||||
}
|
||||
function AttackDoor(){
target = targetDoor;
KFM.Acceleration = vect(0,0,0);
KFM.DoorAttack(target);
|
||||
}
|
||||
function SeePlayer( Pawn Seen ){
if( KFM.intelligence == BRAINS_Human
&& ActorReachable(Seen) && SetEnemy(Seen))
WhatToDoNext(23);
|
||||
}
|
||||
function DamageAttitudeTo(Pawn Other, float Damage){
if( KFM.intelligence >= BRAINS_Mammal && Other != none
&& ActorReachable(Other) && SetEnemy(Other))
WhatToDoNext(32);
|
||||
}
|
||||
function HearNoise(float Loudness, Actor NoiseMaker){
if( KFM.intelligence == BRAINS_Human && NoiseMaker != none
&& NoiseMaker.Instigator != none
&& ActorReachable(NoiseMaker.Instigator)
&& SetEnemy(NoiseMaker.Instigator))
WhatToDoNext(32);
|
||||
}
|
||||
function Tick(float delta){
Global.Tick(delta);
|
||||
// Don't move while we are bashing a door!
moveTarget = none;
moveTimer = -1;
pawn.acceleration = vect(0,0,0);
pawn.groundSpeed = 1;
pawn.accelRate = 0;
|
||||
}
|
||||
function EndState(){
if(NiceMonster(pawn) != none){
pawn.accelRate = pawn.default.accelRate;
pawn.groundSpeed = NiceMonster(pawn).GetOriginalGroundSpeed();
}
|
||||
}
|
||||
Begin:
|
||||
WaitForLanding();
|
||||
KeepMoving:
|
||||
while(KFM.bShotAnim)
Sleep(0.25);
|
||||
while( TargetDoor != none && !TargetDoor.bHidden && TargetDoor.bSealed
&& !TargetDoor.bZombiesIgnore){
AttackDoor();
while(KFM.bShotAnim)
Sleep(0.25);
Sleep(0.1);
if( KFM.intelligence >= BRAINS_Mammal && Enemy!=none
&& ActorReachable(Enemy) )
WhatToDoNext(14);
|
||||
}
|
||||
WhatToDoNext(152);
|
||||
Moving:
|
||||
MoveToward(TargetDoor);
|
||||
WhatToDoNext(17);
|
||||
if(bSoaking)
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state Freeze{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function BeginState(){
bUseFreezeHack = false;
|
||||
}
|
||||
function Tick(float delta){
Global.Tick(delta);
if(bUseFreezeHack){
moveTarget = none;
moveTimer = -1;
pawn.acceleration = vect(0,0,0);
pawn.groundSpeed = 1;
pawn.accelRate = 0;
}
|
||||
}
|
||||
function EndState(){
if(pawn != none){
pawn.accelRate = pawn.default.AccelRate;
pawn.groundSpeed = NiceMonster(pawn).GetOriginalGroundSpeed();
}
bUseFreezeHack = false;
if(enemy == none)
FindNewEnemy();
if(choosingAttackLevel == 0)
WhatToDoNext(99);
|
||||
}
|
||||
}
|
||||
state WaitForAnim{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
event AnimEnd(int Channel){
pawn.AnimEnd(Channel);
if ( !Monster(pawn).bShotAnim )
WhatToDoNext(99);
|
||||
}
|
||||
function BeginState(){
bUseFreezeHack = False;
|
||||
}
|
||||
function Tick( float Delta ){
Global.Tick(Delta);
if( bUseFreezeHack )
{
MoveTarget = none;
MoveTimer = -1;
pawn.acceleration = vect(0,0,0);
pawn.groundSpeed = 1;
pawn.accelRate = 0;
}
|
||||
}
|
||||
function EndState(){
if(NiceMonster(pawn) != none){
pawn.accelRate = pawn.Default.AccelRate;
pawn.groundSpeed = NiceMonster(pawn).GetOriginalGroundSpeed();
}
bUseFreezeHack = False;
|
||||
}
|
||||
Begin:
|
||||
while(KFM.bShotAnim){
Sleep(0.15);
|
||||
}
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
function bool SetEnemy( pawn newEnemy,
optional bool bHateMonster,
optional float MonsterHateChanceOverride){
|
||||
local NiceMonster niceZed;
|
||||
local bool bCanForceFight;
|
||||
// Can we fight anything?
|
||||
niceZed = NiceMonster(pawn);
|
||||
if(niceZed != none)
bCanForceFight =
KFMonster(pawn).HeadHealth <= 0
|| KFMonster(pawn).bDecapitated
|| newEnemy.Health <= 15;
|
||||
if(newEnemy != none)
bCanForceFight = bCanForceFight
&& newEnemy.Health > 0 && newEnemy != enemy;
|
||||
else
bCanForceFight = false;
|
||||
// Do fight if we can
|
||||
if(bCanForceFight){
ChangeEnemy(newEnemy, true);
FightEnemy(false);
return true;
|
||||
}
|
||||
// Otherwise - do the usual stupid stuff
|
||||
return super.SetEnemy(newEnemy, bHateMonster, monsterHateChanceOverride);
|
||||
}
|
||||
simulated function AddKillAssistant(Controller PC, float damage){
|
||||
local bool bIsalreadyAssistant;
|
||||
local int i;
|
||||
if(PC == none) return;
|
||||
for(i = 0;i < KillAssistants.length;i ++)
if(PC == KillAssistants[i].PC){
bIsalreadyAssistant = true;
KillAssistants[i].damage += damage;
break;
}
|
||||
if(!bIsalreadyAssistant){
KillAssistants.Insert(0, 1);
KillAssistants[0].PC = PC;
KillAssistants[0].damage = damage;
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
//==============================================================================
|
||||
// NicePack / NiceMonsterController
|
||||
//==============================================================================
|
||||
// New base class for zeds that makes it easier to implement various changes
|
||||
// and bug fixes.
|
||||
// Functionality:
|
||||
// - Removed threat assessment functionality in favor of vanilla's
|
||||
// distance-based behavior
|
||||
// - Doesn't support 'bNoAutoHuntEnemies' flag from 'KFMonster'
|
||||
//==============================================================================
|
||||
// 'Nice pack' source
|
||||
// Do whatever the fuck you want with it
|
||||
// Author: dkanus
|
||||
// E-mail: dkanus@gmail.com
|
||||
//==============================================================================
|
||||
class NiceMonsterController extends KFMonsterController;
|
||||
// Just reset threat assesment flag, since it's not used in NicePack
|
||||
function PostBeginPlay(){
|
||||
super.PostBeginPlay();
|
||||
bUseThreatAssessment = true;
|
||||
}
|
||||
event bool NotifyBump(Actor other){
|
||||
local Pawn otherPawn;
|
||||
Disable('NotifyBump');
|
||||
otherPawn = Pawn(other);
|
||||
if(otherPawn == none || otherPawn.controller == none) return false;
|
||||
if(enemy == otherPawn) return false;
|
||||
if(SetEnemy(otherPawn)){
|
||||
WhatToDoNext(4);
|
||||
return false;
|
||||
}
|
||||
if(enemy == otherPawn) return false;
|
||||
if(!AdjustAround(otherPawn))
|
||||
CancelCampFor(otherPawn.controller);
|
||||
return false;
|
||||
}
|
||||
state Startled{
|
||||
ignores EnemyNotVisible,SeePlayer,HearNoise;
|
||||
function Startle(Actor Feared){
|
||||
goalString = "STARTLED!";
|
||||
startleActor = feared;
|
||||
BeginState();
|
||||
}
|
||||
function BeginState(){
|
||||
if(startleActor == none){
|
||||
GotoState('');
|
||||
return;
|
||||
}
|
||||
pawn.acceleration = pawn.location - startleActor.location;
|
||||
pawn.acceleration.Z = 0;
|
||||
pawn.bIsWalking = false;
|
||||
pawn.bWantsToCrouch = false;
|
||||
if(pawn.acceleration == vect(0,0,0))
|
||||
pawn.acceleration = VRand();
|
||||
pawn.acceleration = pawn.accelRate * Normal(pawn.acceleration);
|
||||
}
|
||||
Begin:
|
||||
if( NiceHumanPawn(StartleActor) == none
|
||||
|| KFGameType(Level.Game) == none
|
||||
|| KFGameType(Level.Game).bZEDTimeActive ){
|
||||
Sleep(0.5);
|
||||
WhatToDoNext(11);
|
||||
}
|
||||
else{
|
||||
Sleep(0.25);
|
||||
Goto('Begin');
|
||||
}
|
||||
}
|
||||
function bool IsMonsterDecapitated(){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(self.pawn);
|
||||
if(niceZed == none) return false;
|
||||
return niceZed.bDecapitated || niceZed.HeadHealth <= 0;
|
||||
}
|
||||
function bool IsMonsterMad(){
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(self.pawn);
|
||||
if(niceZed == none) return false;
|
||||
return niceZed.madnessCountDown > 0.0;
|
||||
}
|
||||
function bool FindNewEnemy(){
|
||||
local bool bSeeBest;
|
||||
local bool bDecapitated, bAttacksAnything;
|
||||
local float bestScore, newScore;
|
||||
local Pawn bestEnemy;
|
||||
local Controller ctrlIter;
|
||||
if(pawn == none) return false;
|
||||
bDecapitated = IsMonsterDecapitated();
|
||||
bAttacksAnything = bDecapitated || IsMonsterMad();
|
||||
for(ctrlIter = Level.controllerList;
|
||||
ctrlIter != none;
|
||||
ctrlIter = ctrlIter.nextController){
|
||||
if(ctrlIter == none || ctrlIter.pawn == none) continue;
|
||||
if(ctrlIter.pawn.health <= 0 || ctrlIter.pawn == self.pawn) continue;
|
||||
if(ctrlIter.bPendingDelete || ctrlIter.pawn.bPendingDelete) continue;
|
||||
// Shouldn't normally attack healthy zeds
|
||||
if( !bAttacksAnything && NiceMonster(ctrlIter.pawn) != none
|
||||
&& ctrlIter.pawn.health > 15) continue;
|
||||
// Can only stand up to fleshpound if we're decapitated
|
||||
if(NiceZombieFleshpound(ctrlIter.pawn) != none && !bDecapitated)
|
||||
continue;
|
||||
|
||||
// NicePack doesn't use threat assesment, so just find closest target
|
||||
newScore = VSizeSquared(ctrlIter.pawn.Location - pawn.Location);
|
||||
if(bestEnemy == none || newScore < bestScore){
|
||||
bestEnemy = ctrlIter.pawn;
|
||||
bestScore = newScore;
|
||||
bSeeBest = CanSee(bestEnemy);
|
||||
}
|
||||
}
|
||||
if(bestEnemy == enemy) return false;
|
||||
if(bestEnemy != none){
|
||||
ChangeEnemy(bestEnemy, bSeeBest);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function UpdatePathFindState(){
|
||||
if(pathFindState == 0){
|
||||
initialPathGoal = FindRandomDest();
|
||||
pathFindState = 1;
|
||||
}
|
||||
if(pathFindState == 1){
|
||||
if(initialPathGoal == none)
|
||||
pathFindState = 2;
|
||||
else if(ActorReachable(initialPathGoal)){
|
||||
MoveTarget = initialPathGoal;
|
||||
pathFindState = 2;
|
||||
return;
|
||||
}
|
||||
else if(FindBestPathToward(initialPathGoal, true, true))
|
||||
return;
|
||||
else
|
||||
pathFindState = 2;
|
||||
}
|
||||
}
|
||||
function PickRandomDestination(){
|
||||
local bool bCloseToEnemy;
|
||||
local bool bCanTrackEnemy;
|
||||
local NiceMonster niceZed;
|
||||
niceZed = NiceMonster(pawn);
|
||||
if(niceZed == none) return;
|
||||
if(enemy != none)
|
||||
bCloseToEnemy = VSizeSquared(niceZed.Location - enemy.Location) < 40000;
|
||||
// Can we track our enemy?
|
||||
if(enemy != none && niceZed.headHealth > 0 && FRand() < 0.5){
|
||||
bCanTrackEnemy = MoveTarget == enemy;
|
||||
if(!ActorReachable(enemy))
|
||||
bCanTrackEnemy = false;
|
||||
if(niceZed.default.health < 500 && !bCloseToEnemy)
|
||||
bCanTrackEnemy = false;
|
||||
}
|
||||
// Choose random location
|
||||
if(bCanTrackEnemy)
|
||||
destination = enemy.location + VRand() * 50;
|
||||
else
|
||||
destination = niceZed.location + VRand() * 200;
|
||||
}
|
||||
state ZombieHunt{
|
||||
function BeginState(){
|
||||
local float zDif;
|
||||
|
||||
if(pawn.collisionRadius > 27 || pawn.collisionHeight > 46){
|
||||
zDif = Pawn.collisionHeight - 44;
|
||||
Pawn.SetCollisionSize(24, 44);
|
||||
Pawn.MoveSmooth(vect(0,0,-1) * zDif);
|
||||
}
|
||||
}
|
||||
function EndState(){
|
||||
local float zDif;
|
||||
local bool bCollisionSizeChanged;
|
||||
|
||||
bCollisionSizeChanged =
|
||||
pawn.collisionRadius != pawn.default.collisionRadius;
|
||||
bCollisionSizeChanged = bCollisionSizeChanged ||
|
||||
pawn.collisionHeight != pawn.default.collisionHeight;
|
||||
|
||||
if(pawn != none && bCollisionSizeChanged){
|
||||
zDif = pawn.Default.collisionRadius - 44;
|
||||
pawn.MoveSmooth(vect(0,0,1) * zDif);
|
||||
pawn.SetCollisionSize( pawn.Default.collisionRadius,
|
||||
pawn.Default.collisionHeight);
|
||||
}
|
||||
}
|
||||
function Timer(){
|
||||
if(pawn.Velocity == vect(0,0,0))
|
||||
GotoState('ZombieRestFormation', 'Moving');
|
||||
SetCombatTimer();
|
||||
StopFiring();
|
||||
}
|
||||
function PickDestinationEnemyDied(){
|
||||
}
|
||||
function PickDestination(){
|
||||
// Change behaviour in case we're 'BRAINS_Retarded'
|
||||
if(KFM.intelligence == BRAINS_Retarded){
|
||||
// Some of the TWI's code
|
||||
if(FindFreshBody()) return;
|
||||
if(enemy != none && !KFM.bCannibal && enemy.health <= 0){
|
||||
enemy = none;
|
||||
WhatToDoNext(23);
|
||||
return;
|
||||
}
|
||||
UpdatePathFindState();
|
||||
if(pawn.JumpZ > 0)
|
||||
pawn.bCanJump = true;
|
||||
// And just pick random location
|
||||
PickRandomDestination();
|
||||
return;
|
||||
}
|
||||
else
|
||||
super.PickDestination();
|
||||
}
|
||||
}
|
||||
function NotifyTakeHit( Pawn InstigatedBy,
|
||||
Vector HitLocation,
|
||||
int damage,
|
||||
class<DamageType> damageType,
|
||||
Vector momentum){
|
||||
local KFMonster zed;
|
||||
local bool bZedCanVomit;
|
||||
if(class<DamTypeBlowerThrower>(damageType) == none || damage <= 0) return;
|
||||
foreach VisibleCollidingActors(class'KFMonster', zed, 1000, pawn.location){
|
||||
bZedCanVomit = zed.IsA('NiceZombieBloatBase');
|
||||
bZedCanVomit = bZedCanVomit || zed.IsA('NiceZombieSickBase');
|
||||
if(bZedCanVomit && zed != pawn && KFHumanPawn(instigatedBy) != none){
|
||||
if(KFMonster(pawn) != none)
|
||||
SetEnemy(zed, true, KFMonster(pawn).HumanBileAggroChance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.NotifyTakeHit(InstigatedBy,HitLocation, damage, damageType, momentum);
|
||||
}
|
||||
state ZombieCharge{
|
||||
function SeePlayer(Pawn seen){
|
||||
if(KFM.intelligence == BRAINS_Human)
|
||||
SetEnemy(Seen);
|
||||
}
|
||||
function DamageAttitudeTo(Pawn other, float damage){
|
||||
if(KFM.intelligence >= BRAINS_Mammal && other!=none && SetEnemy(other))
|
||||
SetEnemy(other);
|
||||
}
|
||||
function HearNoise(float loudness, Actor noiseMaker){
|
||||
if(KFM.intelligence != BRAINS_Human) return;
|
||||
if(noiseMaker == none && noiseMaker.Instigator == none) return;
|
||||
|
||||
if(FastTrace(noiseMaker.location, pawn.location))
|
||||
SetEnemy(noiseMaker.Instigator);
|
||||
}
|
||||
function bool StrafeFromDamage( float damage,
|
||||
class<DamageType> damageType,
|
||||
bool bFindDest){
|
||||
return false;
|
||||
}
|
||||
function bool TryStrafe(vector sideDir){
|
||||
return false;
|
||||
}
|
||||
Begin:
|
||||
if(pawn.physics == PHYS_Falling){
|
||||
focus = enemy;
|
||||
destination = enemy.location;
|
||||
WaitForLanding();
|
||||
}
|
||||
if(enemy == none)
|
||||
WhatToDoNext(16);
|
||||
WaitForAnim:
|
||||
while(KFM.bShotAnim)
|
||||
Sleep(0.35);
|
||||
if(!FindBestPathToward(enemy, false, true))
|
||||
GotoState('TacticalMove');
|
||||
Moving:
|
||||
if(KFM.intelligence == BRAINS_Retarded){
|
||||
if( KFMonster(pawn).HeadHealth > 0 && moveTarget == enemy
|
||||
&& FRand() < 0.5
|
||||
&& ( KFMonster(pawn).default.Health >= 500
|
||||
|| VSize(pawn.location - moveTarget.location) < 200)
|
||||
)
|
||||
MoveTo(moveTarget.location + VRand() * 50, none);
|
||||
else
|
||||
MoveTo(pawn.location + VRand() * 200, none);
|
||||
}
|
||||
else
|
||||
MoveToward(moveTarget, FaceActor(1),, ShouldStrafeTo(moveTarget));
|
||||
WhatToDoNext(17);
|
||||
if (bSoaking)
|
||||
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state DoorBashing{
|
||||
ignores EnemyNotVisible,SeeMonster;
|
||||
function Timer(){
|
||||
Disable('NotifyBump');
|
||||
}
|
||||
function AttackDoor(){
|
||||
target = targetDoor;
|
||||
KFM.Acceleration = vect(0,0,0);
|
||||
KFM.DoorAttack(target);
|
||||
}
|
||||
function SeePlayer( Pawn Seen ){
|
||||
if( KFM.intelligence == BRAINS_Human
|
||||
&& ActorReachable(Seen) && SetEnemy(Seen))
|
||||
WhatToDoNext(23);
|
||||
}
|
||||
function DamageAttitudeTo(Pawn Other, float Damage){
|
||||
if( KFM.intelligence >= BRAINS_Mammal && Other != none
|
||||
&& ActorReachable(Other) && SetEnemy(Other))
|
||||
WhatToDoNext(32);
|
||||
}
|
||||
function HearNoise(float Loudness, Actor NoiseMaker){
|
||||
if( KFM.intelligence == BRAINS_Human && NoiseMaker != none
|
||||
&& NoiseMaker.Instigator != none
|
||||
&& ActorReachable(NoiseMaker.Instigator)
|
||||
&& SetEnemy(NoiseMaker.Instigator))
|
||||
WhatToDoNext(32);
|
||||
}
|
||||
function Tick(float delta){
|
||||
Global.Tick(delta);
|
||||
|
||||
// Don't move while we are bashing a door!
|
||||
moveTarget = none;
|
||||
moveTimer = -1;
|
||||
pawn.acceleration = vect(0,0,0);
|
||||
pawn.groundSpeed = 1;
|
||||
pawn.accelRate = 0;
|
||||
}
|
||||
function EndState(){
|
||||
if(NiceMonster(pawn) != none){
|
||||
pawn.accelRate = pawn.default.accelRate;
|
||||
pawn.groundSpeed = NiceMonster(pawn).GetOriginalGroundSpeed();
|
||||
}
|
||||
}
|
||||
Begin:
|
||||
WaitForLanding();
|
||||
KeepMoving:
|
||||
while(KFM.bShotAnim)
|
||||
Sleep(0.25);
|
||||
while( TargetDoor != none && !TargetDoor.bHidden && TargetDoor.bSealed
|
||||
&& !TargetDoor.bZombiesIgnore){
|
||||
AttackDoor();
|
||||
while(KFM.bShotAnim)
|
||||
Sleep(0.25);
|
||||
Sleep(0.1);
|
||||
if( KFM.intelligence >= BRAINS_Mammal && Enemy!=none
|
||||
&& ActorReachable(Enemy) )
|
||||
WhatToDoNext(14);
|
||||
}
|
||||
WhatToDoNext(152);
|
||||
Moving:
|
||||
MoveToward(TargetDoor);
|
||||
WhatToDoNext(17);
|
||||
if(bSoaking)
|
||||
SoakStop("STUCK IN CHARGING!");
|
||||
}
|
||||
state Freeze{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
function BeginState(){
|
||||
bUseFreezeHack = false;
|
||||
}
|
||||
function Tick(float delta){
|
||||
Global.Tick(delta);
|
||||
if(bUseFreezeHack){
|
||||
moveTarget = none;
|
||||
moveTimer = -1;
|
||||
pawn.acceleration = vect(0,0,0);
|
||||
pawn.groundSpeed = 1;
|
||||
pawn.accelRate = 0;
|
||||
}
|
||||
}
|
||||
function EndState(){
|
||||
if(pawn != none){
|
||||
pawn.accelRate = pawn.default.AccelRate;
|
||||
pawn.groundSpeed = NiceMonster(pawn).GetOriginalGroundSpeed();
|
||||
}
|
||||
bUseFreezeHack = false;
|
||||
if(enemy == none)
|
||||
FindNewEnemy();
|
||||
if(choosingAttackLevel == 0)
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
}
|
||||
state WaitForAnim{
|
||||
Ignores SeePlayer,HearNoise,Timer,EnemyNotVisible,NotifyBump,Startle;
|
||||
// Don't do this in this state
|
||||
function GetOutOfTheWayOfShot(vector ShotDirection, vector ShotOrigin){}
|
||||
event AnimEnd(int Channel){
|
||||
pawn.AnimEnd(Channel);
|
||||
if ( !Monster(pawn).bShotAnim )
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
function BeginState(){
|
||||
bUseFreezeHack = False;
|
||||
}
|
||||
function Tick( float Delta ){
|
||||
Global.Tick(Delta);
|
||||
if( bUseFreezeHack )
|
||||
{
|
||||
MoveTarget = none;
|
||||
MoveTimer = -1;
|
||||
pawn.acceleration = vect(0,0,0);
|
||||
pawn.groundSpeed = 1;
|
||||
pawn.accelRate = 0;
|
||||
}
|
||||
}
|
||||
function EndState(){
|
||||
if(NiceMonster(pawn) != none){
|
||||
pawn.accelRate = pawn.Default.AccelRate;
|
||||
pawn.groundSpeed = NiceMonster(pawn).GetOriginalGroundSpeed();
|
||||
}
|
||||
bUseFreezeHack = False;
|
||||
}
|
||||
Begin:
|
||||
while(KFM.bShotAnim){
|
||||
Sleep(0.15);
|
||||
}
|
||||
WhatToDoNext(99);
|
||||
}
|
||||
function bool SetEnemy( pawn newEnemy,
|
||||
optional bool bHateMonster,
|
||||
optional float MonsterHateChanceOverride){
|
||||
local NiceMonster niceZed;
|
||||
local bool bCanForceFight;
|
||||
// Can we fight anything?
|
||||
niceZed = NiceMonster(pawn);
|
||||
if(niceZed != none)
|
||||
bCanForceFight =
|
||||
KFMonster(pawn).HeadHealth <= 0
|
||||
|| KFMonster(pawn).bDecapitated
|
||||
|| newEnemy.Health <= 15;
|
||||
if(newEnemy != none)
|
||||
bCanForceFight = bCanForceFight
|
||||
&& newEnemy.Health > 0 && newEnemy != enemy;
|
||||
else
|
||||
bCanForceFight = false;
|
||||
// Do fight if we can
|
||||
if(bCanForceFight){
|
||||
ChangeEnemy(newEnemy, true);
|
||||
FightEnemy(false);
|
||||
return true;
|
||||
}
|
||||
// Otherwise - do the usual stupid stuff
|
||||
return super.SetEnemy(newEnemy, bHateMonster, monsterHateChanceOverride);
|
||||
}
|
||||
simulated function AddKillAssistant(Controller PC, float damage){
|
||||
local bool bIsalreadyAssistant;
|
||||
local int i;
|
||||
if(PC == none) return;
|
||||
for(i = 0;i < KillAssistants.length;i ++)
|
||||
if(PC == KillAssistants[i].PC){
|
||||
bIsalreadyAssistant = true;
|
||||
KillAssistants[i].damage += damage;
|
||||
break;
|
||||
}
|
||||
if(!bIsalreadyAssistant){
|
||||
KillAssistants.Insert(0, 1);
|
||||
KillAssistants[0].PC = PC;
|
||||
KillAssistants[0].damage = damage;
|
||||
}
|
||||
}
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue