Code: Select all
class QueenSB expands Queen;
/******************************************************************************
Changes to UnrealI.Queen:
1) Queens spawned via factory do not telefrag other queens (based on function
EncroachingOn())
2) Queens try to teleport only once if there is no QueenDest (based on variable
"QueenDestHave").
3) Queens do not telefrag other pawns on QueenDests.
4) *ProjectileSpeed* is used now.
Hint: if you use CheckReplacement() to exchange the Queens, you should also
exchange "Prototype" in factories.
******************************************************************************/
var enum EQueenDestHave {
QDH_Yes, QDH_No, QDH_Unchecked
} QueenDestHave;
event bool EncroachingOn( actor Other ) {
if ( (Other.Brush != None) || (Brush(Other) != None) || Queen(Other) != None)
return true;
return false;
}
function bool RadiusActorHave(Actor A, class<actor> SearchClass, float Radius) {
local Actor Result;
foreach A.RadiusActors(SearchClass, Result, Radius, A.Location)
return true;
return false;
}
function PostBeginPlay() {
local float PreProjectileSpeed;
PreProjectileSpeed = ProjectileSpeed;
Super.PostBeginPlay();
// same as in Queen.PostBeginPlay(), except using *ProjectileSpeed* now instead of "1200":
ProjectileSpeed = PreProjectileSpeed + 100 * Skill;
}
state TacticalMove
{
ignores SeePlayer, HearNoise;
function PickDestination(bool bNoCharge)
{
// copied all variables from ScriptedPawn.TacticalMove.PickDestination(), because "Super(ScriptedPawn).PickDestination(bNoCharge)" does not compile
local vector pickdir, enemydir, enemyPart, Y, minDest;
local actor HitActor;
local vector HitLocation, HitNormal, collSpec;
local float Aggression, enemydist, minDist, strafeSize, optDist;
local bool success, bNoReach;
if ((FRand() < 0.26) && QueenDestHave != QDH_No)
{
GotoState('Teleporting');
Return;
}
// copied all remaining lines from ScriptedPawn.TacticalMove.PickDestination(), because "Super(ScriptedPawn).PickDestination(bNoCharge)" does not compile
bChangeDir = false;
if (Region.Zone.bWaterZone && !bCanSwim && bCanFly)
{
Destination = Location + 75 * (VRand() + vect(0,0,1));
Destination.Z += 100;
return;
}
if ( Enemy.Region.Zone.bWaterZone )
bNoCharge = bNoCharge || !bCanSwim;
else
bNoCharge = bNoCharge || (!bCanFly && !bCanWalk);
success = false;
enemyDist = VSize(Location - Enemy.Location);
Aggression = 2 * (CombatStyle + FRand()) - 1.1;
if ( intelligence == BRAINS_Human )
{
if ( Enemy.bIsPlayer && (AttitudeToPlayer == ATTITUDE_Fear) && (CombatStyle > 0) )
Aggression = Aggression - 2 - 2 * CombatStyle;
if ( Weapon != None )
Aggression += 2 * Weapon.SuggestAttackStyle();
if ( Enemy.Weapon != None )
Aggression += 2 * Enemy.Weapon.SuggestDefenseStyle();
}
if ( enemyDist > 1000 )
Aggression += 1;
if ( bIsPlayer && !bNoCharge )
bNoCharge = ( Aggression < FRand() );
if ( (Physics == PHYS_Walking) || (Physics == PHYS_Falling) )
{
if (Location.Z > Enemy.Location.Z + 140) //tactical height advantage
Aggression = FMax(0.0, Aggression - 1.0 + CombatStyle);
else if (Location.Z < Enemy.Location.Z - CollisionHeight) // below enemy
{
if ( !bNoCharge && (Intelligence > BRAINS_Reptile)
&& (Aggression > 0) && (FRand() < 0.6) )
{
GotoState('Charging');
return;
}
else if ( (enemyDist < 1.1 * (Enemy.Location.Z - Location.Z))
&& !actorReachable(Enemy) )
{
bNoReach = (Intelligence > BRAINS_None);
aggression = -1.5 * FRand();
}
}
}
if (!bNoCharge && (Aggression > 2 * FRand()))
{
if ( bNoReach && (Physics != PHYS_Falling) )
{
TweenToRunning(0.15);
GotoState('Charging', 'NoReach');
}
else
GotoState('Charging');
return;
}
if (enemyDist > FMax(VSize(OldLocation - Enemy.OldLocation), 240))
Aggression += 0.4 * FRand();
enemydir = (Enemy.Location - Location)/enemyDist;
minDist = FMin(160.0, 3*CollisionRadius);
if ( bIsPlayer )
optDist = 80 + FMin(EnemyDist, 250 * (FRand() + FRand()));
else
optDist = 50 + FMin(EnemyDist, 500 * FRand());
Y = (enemydir Cross vect(0,0,1));
if ( Physics == PHYS_Walking )
{
Y.Z = 0;
enemydir.Z = 0;
}
else
enemydir.Z = FMax(0,enemydir.Z);
strafeSize = FMax(-0.7, FMin(0.85, (2 * Aggression * FRand() - 0.3)));
enemyPart = enemydir * strafeSize;
strafeSize = FMax(0.0, 1 - Abs(strafeSize));
pickdir = strafeSize * Y;
if ( bStrafeDir )
pickdir *= -1;
bStrafeDir = !bStrafeDir;
collSpec.X = CollisionRadius;
collSpec.Y = CollisionRadius;
collSpec.Z = FMax(6, CollisionHeight - 18);
minDest = Location + minDist * (pickdir + enemyPart);
HitActor = Trace(HitLocation, HitNormal, minDest, Location, false, collSpec);
if (HitActor == None)
{
success = (Physics != PHYS_Walking);
if ( !success )
{
collSpec.X = FMin(14, 0.5 * CollisionRadius);
collSpec.Y = collSpec.X;
HitActor = Trace(HitLocation, HitNormal, minDest - (18 + MaxStepHeight) * vect(0,0,1), minDest, false, collSpec);
success = (HitActor != None);
}
if (success)
Destination = minDest + (pickdir + enemyPart) * optDist;
}
if ( !success )
{
collSpec.X = CollisionRadius;
collSpec.Y = CollisionRadius;
minDest = Location + minDist * (enemyPart - pickdir);
HitActor = Trace(HitLocation, HitNormal, minDest, Location, false, collSpec);
if (HitActor == None)
{
success = (Physics != PHYS_Walking);
if ( !success )
{
collSpec.X = FMin(14, 0.5 * CollisionRadius);
collSpec.Y = collSpec.X;
HitActor = Trace(HitLocation, HitNormal, minDest - (18 + MaxStepHeight) * vect(0,0,1), minDest, false, collSpec);
success = (HitActor != None);
}
if (success)
Destination = minDest + (enemyPart - pickdir) * optDist;
}
else
{
if ( (CombatStyle <= 0) || (Enemy.bIsPlayer && (AttitudeToPlayer == ATTITUDE_Fear)) )
enemypart = vect(0,0,0);
else if ( (enemydir Dot enemyPart) < 0 )
enemyPart = -1 * enemyPart;
pickDir = Normal(enemyPart - pickdir + HitNormal);
minDest = Location + minDist * pickDir;
collSpec.X = CollisionRadius;
collSpec.Y = CollisionRadius;
HitActor = Trace(HitLocation, HitNormal, minDest, Location, false, collSpec);
if (HitActor == None)
{
success = (Physics != PHYS_Walking);
if ( !success )
{
collSpec.X = FMin(14, 0.5 * CollisionRadius);
collSpec.Y = collSpec.X;
HitActor = Trace(HitLocation, HitNormal, minDest - (18 + MaxStepHeight) * vect(0,0,1), minDest, false, collSpec);
success = (HitActor != None);
}
if (success)
Destination = minDest + pickDir * optDist;
}
}
}
if ( !success )
GiveUpTactical(bNoCharge);
else
{
pickDir = (Destination - Location);
enemyDist = VSize(pickDir);
if ( enemyDist > minDist + 2 * CollisionRadius )
{
pickDir = pickDir/enemyDist;
HitActor = Trace(HitLocation, HitNormal, Destination + 2 * CollisionRadius * pickdir, Location, false);
if ( (HitActor != None) && ((HitNormal Dot pickDir) < -0.6) )
Destination = HitLocation - 2 * CollisionRadius * pickdir;
}
}
}
}
state Hunting
{
ignores EnemyNotVisible;
function PickDestination()
{
// copied all variables from ScriptedPawn.PickDestination(), because "Super(ScriptedPawn).PickDestination(bNoCharge)" does not compile
local NavigationPoint path;
local actor HitActor;
local vector HitNormal, HitLocation, nextSpot, ViewSpot;
local float posZ, elapsed;
local bool bCanSeeLastSeen;
if (QueenDestHave != QDH_No)
{
GotoState('Teleporting');
Return;
}
// copied all remaining lines from ScriptedPawn.Hunting.PickDestination(), because "Super(ScriptedPawn).PickDestination(bNoCharge)" does not compile
// If no enemy, or I should see him but don't, then give up
if ( (Enemy == None) || (Enemy.Health <= 0) )
{
WhatToDoNext('','');
return;
}
bAvoidLedges = false;
elapsed = Level.TimeSeconds - HuntStartTime;
if ( (elapsed > 30) && ((intelligence < BRAINS_Human) || (elapsed > 90)) )
{
WhatToDoNext('','');
return;
}
if ( JumpZ > 0 )
bCanJump = true;
if ( ActorReachable(Enemy) )
{
if ( bIsBoss || (numHuntPaths < 8 + Skill) || (elapsed < 15)
|| ((Normal(Enemy.Location - Location) Dot vector(Rotation)) > -0.5) )
{
Destination = Enemy.Location;
MoveTarget = None;
numHuntPaths++;
}
else
WhatToDoNext('','');
return;
}
numHuntPaths++;
ViewSpot = Location + EyeHeight * vect(0,0,1);
bCanSeeLastSeen = false;
if ( intelligence > BRAINS_Reptile )
{
HitActor = Trace(HitLocation, HitNormal, LastSeenPos, ViewSpot, false);
bCanSeeLastSeen = (HitActor == None);
if ( bCanSeeLastSeen )
{
HitActor = Trace(HitLocation, HitNormal, LastSeenPos, Enemy.Location, false);
bHunting = (HitActor != None);
}
else
bHunting = true;
if ( FindBestPathToward(Enemy) )
return;
}
MoveTarget = None;
if ( bFromWall )
{
bFromWall = false;
if ( !PickWallAdjust() )
{
if ( CanStakeOut() )
GotoState('StakeOut');
else
WhatToDoNext('', '');
}
return;
}
if ( !bIsBoss && (NumHuntPaths > 20) && ((Intelligence < BRAINS_Human) || (NumHuntPaths > 60)) )
{
WhatToDoNext('', '');
return;
}
if ( LastSeeingPos != vect(1000000,0,0) )
{
Destination = LastSeeingPos;
LastSeeingPos = vect(1000000,0,0);
HitActor = Trace(HitLocation, HitNormal, Enemy.Location, ViewSpot, false);
if ( HitActor == None )
{
If (VSize(Location - Destination) < 20)
{
HitActor = Trace(HitLocation, HitNormal, Enemy.Location, ViewSpot, false);
if (HitActor == None)
{
SetEnemy(Enemy);
return;
}
}
return;
}
}
bAvoidLedges = ( (CollisionRadius > 42) && (Intelligence < BRAINS_Human) );
posZ = LastSeenPos.Z + CollisionHeight - Enemy.CollisionHeight;
nextSpot = LastSeenPos - Normal(Enemy.Location - Enemy.OldLocation) * CollisionRadius;
nextSpot.Z = posZ;
HitActor = Trace(HitLocation, HitNormal, nextSpot , ViewSpot, false);
if ( HitActor == None )
Destination = nextSpot;
else if ( bCanSeeLastSeen )
Destination = LastSeenPos;
else
{
Destination = LastSeenPos;
HitActor = Trace(HitLocation, HitNormal, LastSeenPos , ViewSpot, false);
if ( HitActor != None )
{
// check if could adjust and see it
if ( PickWallAdjust() || FindViewSpot() )
GotoState('Hunting', 'AdjustFromWall');
else if ( bIsBoss || VSize(Enemy.Location - Location) < 1200 )
GotoState('StakeOut');
else
{
WhatToDoNext('Waiting', 'TurnFromWall');
return;
}
}
}
LastSeenPos = Enemy.Location;
}
}
State Teleporting
{
ignores TakeDamage, SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, Died;
function ChooseDestination()
{
local NavigationPoint N;
local vector ViewPoint, HitLocation, HitNormal, Best;
local actor HitActor;
local float rating, newrating, NeededRadius;
Best = Location;
if (QueenDestHave != QDH_No)
{
N = Level.NavigationPointList;
rating = 0;
NeededRadius = fMax(CollisionRadius, CollisionHeight) * 1.5; // 1.5 => keep some safaty distance
QueenDestHave = QDH_No; // will be changed in loop, if a QueenDest is found
while ( N != None )
{
if ( N.IsA('QueenDest'))
{
QueenDestHave = QDH_Yes;
if ( ! RadiusActorHave(N, class'Pawn' , NeededRadius) ) // don't telefrag other Pawns
{ // rate it
newrating = 0;
if ( Best == Location )
Best = N.Location;
ViewPoint = N.Location + EyeHeight * vect(0,0,1);
HitActor = Trace(HitLocation, HitNormal, Enemy.Location, ViewPoint, false);
if ( HitActor == None )
newrating = 20000;
newrating = newrating - VSize(N.Location - Enemy.Location) + 1000 * FRand() + 4 * VSize(N.Location - Location);
if ( N.Location.Z > Enemy.Location.Z )
newrating += 1000;
if ( newrating > rating )
{
rating = newrating;
Best = N.Location;
}
}
}
N = N.nextNavigationPoint;
}
}
TelepDest = Best;
}
}
defaultproperties
{
MenuName="Queen"
ProjectileSpeed=1200
CollisionRadius=90
CollisionHeight=107
}