3178 lines
88 KiB
Ucode
3178 lines
88 KiB
Ucode
//=============================================================================
|
|
// Pawn, the base class of all actors that can be controlled by players or AI.
|
|
//
|
|
// Pawns are the physical representations of players and creatures in a level.
|
|
// Pawns have a mesh, collision, and physics. Pawns can take damage, make sounds,
|
|
// and hold weapons and other inventory. In short, they are responsible for all
|
|
// physical interaction between the player or AI and the world.
|
|
//
|
|
// This is a built-in Unreal class and it shouldn't be modified.
|
|
//=============================================================================
|
|
class Pawn extends Actor
|
|
abstract
|
|
native
|
|
placeable
|
|
config(user)
|
|
nativereplication
|
|
exportstructs;
|
|
|
|
#exec Texture Import File=Textures\Pawn.pcx Name=S_Pawn Mips=Off MASKED=1
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Pawn variables.
|
|
|
|
var Controller Controller;
|
|
|
|
// cache net relevancy test
|
|
var float NetRelevancyTime;
|
|
var playerController LastRealViewer;
|
|
var actor LastViewer;
|
|
|
|
var const float LastLocTime; // used to force periodic location replication, even when pawn isn't moving
|
|
|
|
// Physics related flags.
|
|
var bool bJustLanded; // used by eyeheight adjustment
|
|
var bool bLandRecovery; // used by eyeheight adjustment
|
|
var bool bUpAndOut; // used by swimming
|
|
var bool bIsWalking; // currently walking (can't jump, affects animations)
|
|
var bool bWarping; // Set when travelling through warpzone (so shouldn't telefrag)
|
|
var bool bWantsToCrouch; // if true crouched (physics will automatically reduce collision height to CrouchHeight)
|
|
var const bool bIsCrouched; // set by physics to specify that pawn is currently crouched
|
|
var const bool bTryToUncrouch; // when auto-crouch during movement, continually try to uncrouch
|
|
var() bool bCanCrouch; // if true, this pawn is capable of crouching
|
|
var bool bCrawler; // crawling - pitch and roll based on surface pawn is on
|
|
var const bool bReducedSpeed; // used by movement natives
|
|
var bool bJumpCapable;
|
|
var bool bCanJump; // movement capabilities - used by AI
|
|
var bool bCanWalk;
|
|
var bool bCanSwim;
|
|
var bool bCanFly;
|
|
var bool bCanClimbLadders;
|
|
var bool bCanStrafe;
|
|
var bool bCanDoubleJump;
|
|
var bool bCanWallDodge;
|
|
var bool bAvoidLedges; // don't get too close to ledges
|
|
var bool bStopAtLedges; // if bAvoidLedges and bStopAtLedges, Pawn doesn't try to walk along the edge at all
|
|
var bool bNoJumpAdjust; // set to tell controller not to modify velocity of a jump/fall
|
|
var bool bCountJumps; // if true, inventory wants message whenever this pawn jumps
|
|
var const bool bSimulateGravity; // simulate gravity for this pawn on network clients when predicting position (true if pawn is walking or falling)
|
|
var bool bUpdateEyeheight; // if true, UpdateEyeheight will get called every tick
|
|
var bool bIgnoreForces; // if true, not affected by external forces
|
|
var const bool bNoVelocityUpdate; // used by C++ physics
|
|
var bool bCanWalkOffLedges; // Can still fall off ledges, even when walking (for Player Controlled pawns)
|
|
var bool bSteadyFiring; // used for third person weapon anims/effects
|
|
var bool bCanBeBaseForPawns; // all your 'base', are belong to us
|
|
var bool bClientCollision; // used on clients when temporarily turning off collision
|
|
var const bool bSimGravityDisabled; // used on network clients
|
|
var bool bDirectHitWall; // always call pawn hitwall directly (no controller notifyhitwall)
|
|
var bool bServerMoveSetPawnRot;
|
|
var bool bFlyingKarma; // Tells AI that this vehicle can be flown like PHYS_Flying even though it has karma physics
|
|
var bool bDrawCorona;
|
|
var globalconfig bool bNoCoronas;
|
|
|
|
// used by dead pawns (for bodies landing and changing collision box)
|
|
var bool bThumped;
|
|
var bool bInvulnerableBody;
|
|
|
|
// AI related flags
|
|
var bool bIsFemale;
|
|
var bool bAutoActivate; // if true, automatically activate Powerups which have their bAutoActivate==true
|
|
var bool bCanPickupInventory; // if true, will pickup inventory when touching pickup actors
|
|
var bool bUpdatingDisplay; // to avoid infinite recursion through inventory setdisplay
|
|
var bool bAmbientCreature; // AIs will ignore me
|
|
var(AI) bool bLOSHearing; // can hear sounds from line-of-sight sources (which are close enough to hear)
|
|
// bLOSHearing=true is like UT/Unreal hearing
|
|
var(AI) bool bSameZoneHearing; // can hear any sound in same zone (if close enough to hear)
|
|
var(AI) bool bAdjacentZoneHearing; // can hear any sound in adjacent zone (if close enough to hear)
|
|
var(AI) bool bMuffledHearing; // can hear sounds through walls (but muffled - sound distance increased to double plus 4x the distance through walls
|
|
var(AI) bool bAroundCornerHearing; // Hear sounds around one corner (slightly more expensive, and bLOSHearing must also be true)
|
|
var(AI) bool bDontPossess; // if true, Pawn won't be possessed at game start
|
|
var bool bAutoFire; // used for third person weapon anims/effects
|
|
var bool bRollToDesired; // Update roll when turning to desired rotation (normally false)
|
|
var bool bIgnorePlayFiring; // if true, ignore the next PlayFiring() call (used by AnimNotify_FireWeapon)
|
|
var bool bStationary; // pawn can't move
|
|
|
|
var bool bCachedRelevant; // network relevancy caching flag
|
|
var bool bUseCompressedPosition; // use compressed position in networking - true unless want to replicate roll, or very high velocities
|
|
// if _RO_
|
|
var bool bWeaponBob;
|
|
// else
|
|
//var globalconfig bool bWeaponBob;
|
|
// endif _RO_
|
|
var bool bHideRegularHUD;
|
|
var bool bSpecialHUD;
|
|
var bool bSpecialCrosshair;
|
|
var bool bSpecialCalcView; // If true, the Controller controlling this pawn will call 'SpecialCalcView' to find camera pos.
|
|
var bool bNoTeamBeacon; // never display team beacon for this pawn
|
|
var bool bNoWeaponFiring;
|
|
var bool bIsTyping; // play typing anim if idle
|
|
var bool bScriptPostRender; // if true, PostRender2D() gets called instead of native team beacon drawing code
|
|
var bool bCanUse; // can this pawn Use things?
|
|
var bool bSuperSize; // hack for Leviathan
|
|
|
|
var byte FlashCount; // used for third person weapon anims/effects
|
|
// AI basics.
|
|
var byte Visibility; //How visible is the pawn? 0=invisible, 128=normal, 255=highly visible
|
|
|
|
var float DesiredSpeed;
|
|
var float MaxDesiredSpeed;
|
|
var(AI) name AIScriptTag; // tag of AIScript which should be associated with this pawn
|
|
var(AI) float HearingThreshold; // max distance at which a makenoise(1.0) loudness sound can be heard
|
|
var(AI) float Alertness; // -1 to 1 ->Used within specific states for varying reaction to stimuli
|
|
var(AI) float SightRadius; // Maximum seeing distance.
|
|
var(AI) float PeripheralVision; // Cosine of limits of peripheral vision.
|
|
var() float SkillModifier; // skill modifier (same scale as game difficulty)
|
|
var const float AvgPhysicsTime; // Physics updating time monitoring (for AI monitoring reaching destinations)
|
|
var float MeleeRange; // Max range for melee attack (not including collision radii)
|
|
var float NavigationPointRange; // extra slack to have "reached" a NavigationPoint
|
|
var NavigationPoint Anchor; // current nearest path;
|
|
var const NavigationPoint LastAnchor; // recent nearest path
|
|
var float FindAnchorFailedTime; // last time a FindPath() attempt failed to find an anchor.
|
|
var float LastValidAnchorTime; // last time a valid anchor was found
|
|
var float DestinationOffset; // used to vary destination over NavigationPoints
|
|
var float NextPathRadius; // radius of next path in route
|
|
var vector SerpentineDir; // serpentine direction
|
|
var float SerpentineDist;
|
|
var float SerpentineTime; // how long to stay straight before strafing again
|
|
var const float UncrouchTime; // when auto-crouch during movement, continually try to uncrouch once this decrements to zero
|
|
var float SpawnTime;
|
|
|
|
// Movement.
|
|
var float GroundSpeed; // The maximum ground speed.
|
|
var float WaterSpeed; // The maximum swimming speed.
|
|
var float AirSpeed; // The maximum flying speed.
|
|
var float LadderSpeed; // Ladder climbing speed
|
|
var float AccelRate; // max acceleration rate
|
|
var float JumpZ; // vertical acceleration w/ jump
|
|
var float AirControl; // amount of AirControl available to the pawn
|
|
var float WalkingPct; // pct. of running speed that walking speed is
|
|
var float CrouchedPct; // pct. of running speed that crouched walking speed is
|
|
var float MaxFallSpeed; // max speed pawn can land without taking damage (also limits what paths AI can use)
|
|
var vector ConstantAcceleration; // acceleration added to pawn when falling
|
|
var Vehicle DrivenVehicle;
|
|
var vector ImpactVelocity; // velocity added while falling (bot tries to correct for it)
|
|
var() int PitchUpLimit;
|
|
var() int PitchDownLimit;
|
|
|
|
// Player info.
|
|
var string OwnerName; // Name of owning player (for save games, coop)
|
|
var travel Weapon Weapon; // The pawn's current weapon.
|
|
var Weapon PendingWeapon; // Will become weapon once current weapon is put down
|
|
var travel Powerups SelectedItem; // currently selected inventory item
|
|
var float BaseEyeHeight; // Base eye height above collision center.
|
|
var float EyeHeight; // Current eye height, adjusted for bobbing and stairs.
|
|
var vector Floor; // Normal of floor pawn is standing on (only used by PHYS_Spider and PHYS_Walking)
|
|
var float SplashTime; // time of last splash
|
|
var float CrouchHeight; // CollisionHeight when crouching
|
|
var float CrouchRadius; // CollisionRadius when crouching
|
|
var() float DrivingHeight; // CollisionHeight when driving a vehicle
|
|
var() float DrivingRadius; // CollisionRadius when driving a vehicle
|
|
var float OldZ; // Old Z Location - used for eyeheight smoothing
|
|
var PhysicsVolume HeadVolume; // physics volume of head
|
|
var float HealthMax;
|
|
var float SuperHealthMax;
|
|
var travel int Health; // Health: 100 = normal maximum
|
|
var float BreathTime; // used for getting BreathTimer() messages (for no air, etc.)
|
|
var float UnderWaterTime; // how much time pawn can go without air (in seconds)
|
|
var float LastPainTime; // last time pawn played a takehit animation (updated in PlayHit())
|
|
var class<DamageType> ReducedDamageType; // which damagetype this creature is protected from (used by AI)
|
|
var float HeadRadius; // Squared radius of the head of the pawn that is vulnerable to headshots
|
|
var float HeadHeight; // Distance from base of neck to center of head - used for headshot calculation
|
|
var float HeadScale;
|
|
var bool bSetPCRotOnPossess;
|
|
|
|
// Sound and noise management
|
|
// remember location and position of last noises propagated
|
|
var const vector noise1spot;
|
|
var const float noise1time;
|
|
var const pawn noise1other;
|
|
var const float noise1loudness;
|
|
var const vector noise2spot;
|
|
var const float noise2time;
|
|
var const pawn noise2other;
|
|
var const float noise2loudness;
|
|
var float LastPainSound;
|
|
|
|
// view bob
|
|
// if _RO_
|
|
var float Bob;
|
|
// else
|
|
//var globalconfig float Bob;
|
|
// endif _RO_
|
|
var float LandBob, AppliedBob;
|
|
var float bobtime;
|
|
var vector WalkBob;
|
|
|
|
var float SoundDampening;
|
|
var float DamageScaling;
|
|
// if _KF_
|
|
var(Sound) float AmbientSoundScaling; // General scaling value for this pawn's ambient sounds. We don't want it config so we can modify it per pawn type - Ramm
|
|
//else
|
|
//var globalconfig float AmbientSoundScaling;
|
|
// endif _KF_
|
|
|
|
var localized string MenuName; // Name used for this pawn type in menus (e.g. player selection)
|
|
|
|
// shadow decal
|
|
var Projector Shadow;
|
|
|
|
// blood effect
|
|
var class<Effects> BloodEffect;
|
|
var class<Effects> LowGoreBlood;
|
|
|
|
var class<AIController> ControllerClass; // default class to use when pawn is controlled by AI (can be modified by an AIScript)
|
|
|
|
var PlayerReplicationInfo PlayerReplicationInfo;
|
|
|
|
var LadderVolume OnLadder; // ladder currently being climbed
|
|
|
|
var name LandMovementState; // PlayerControllerState to use when moving on land or air
|
|
var name WaterMovementState; // PlayerControllerState to use when moving in water
|
|
|
|
var PlayerStart LastStartSpot; // used to avoid spawn camping
|
|
var float LastStartTime;
|
|
|
|
// Animation status
|
|
var name AnimAction; // use for replicating anims
|
|
|
|
// Animation updating by physics
|
|
// Note that animation channels 2 through 11 are used for animation updating
|
|
var vector TakeHitLocation; // location of last hit (for playing hit/death anims)
|
|
var class<DamageType> HitDamageType; // damage type of last hit (for playing hit/death anims)
|
|
var vector TearOffMomentum; // momentum to apply when torn off (bTearOff == true)
|
|
var EPhysics OldPhysics;
|
|
var bool bPhysicsAnimUpdate;
|
|
var bool bWasCrouched;
|
|
var bool bWasWalking;
|
|
var bool bWasOnGround;
|
|
var bool bInitializeAnimation;
|
|
var bool bPlayedDeath;
|
|
|
|
// jjs - physics based animation stuff
|
|
var bool bIsIdle; // true when standing still on the ground, Physics can be used for determining other states
|
|
var bool bWaitForAnim; // true if the pawn is playing an important non-looping animation (eg. landing/dodge) and doesn't feel like being interrupted
|
|
var const bool bReverseRun;
|
|
var bool bDoTorsoTwist;
|
|
var const bool FootTurning;
|
|
var const bool FootStill;
|
|
|
|
var const byte ViewPitch; // jjs - something to replicate so we can see which way remote clients are looking
|
|
var int SmoothViewPitch;
|
|
var int SmoothViewYaw;
|
|
|
|
var float OldRotYaw; // used for determining if pawn is turning
|
|
var vector OldAcceleration;
|
|
|
|
// if RO
|
|
var name MovementAnims[8]; // Forward, Back, Left, Right, Forward-Left, Forward-Right, Back-Left, Back-Right
|
|
//else
|
|
//var name MovementAnims[4]; // Forward, Back, Left, Right
|
|
var name TurnLeftAnim;
|
|
var name TurnRightAnim; // turning anims when standing in place (scaled by turn speed)
|
|
var(AnimTweaks) float BlendChangeTime; // time to blend between movement animations
|
|
var float MovementBlendStartTime; // used for delaying the start of run blending
|
|
var float ForwardStrafeBias; // bias of strafe blending in forward direction
|
|
var float BackwardStrafeBias; // bias of strafe blending in backward direction
|
|
|
|
var float DodgeSpeedFactor; // dodge speed moved here so animation knows the diff between a jump and a dodge
|
|
var float DodgeSpeedZ;
|
|
|
|
var const int OldAnimDir;
|
|
var const Vector OldVelocity;
|
|
var float IdleTime;
|
|
|
|
var name SwimAnims[4]; // 0=forward, 1=backwards, 2=left, 3=right
|
|
|
|
// if RO
|
|
var name CrouchAnims[8]; // Forward, Back, Left, Right, Forward-Left, Forward-Right, Back-Left, Back-Right
|
|
var name WalkAnims[8];
|
|
//else
|
|
//var name CrouchAnims[4];
|
|
//var name WalkAnims[4];
|
|
var name AirAnims[4];
|
|
var name TakeoffAnims[4];
|
|
var name LandAnims[4];
|
|
var name DoubleJumpAnims[4];
|
|
var name DodgeAnims[4];
|
|
var name AirStillAnim;
|
|
var name TakeoffStillAnim;
|
|
var name CrouchTurnRightAnim;
|
|
var name CrouchTurnLeftAnim;
|
|
var name IdleCrouchAnim;
|
|
var name IdleSwimAnim;
|
|
var name IdleWeaponAnim; // WeaponAttachment code will set this one
|
|
var name IdleRestAnim;
|
|
var name IdleChatAnim;
|
|
|
|
var array<name> TauntAnims; // Array of names of taunt anim that can be played by this character. First 4 assumed to be orders.
|
|
var localized string TauntAnimNames[16]; // Text description
|
|
|
|
var const int FootRot; // torso twisting/looking stuff
|
|
var const int TurnDir;
|
|
var name RootBone;
|
|
var name HeadBone;
|
|
var name SpineBone1;
|
|
var name SpineBone2;
|
|
|
|
// xPawn replicated properties - moved here to take advantage of native replication
|
|
var(Shield) transient float ShieldStrength; // current shielding (having been activated)
|
|
|
|
struct HitFXData
|
|
{
|
|
var() Name Bone;
|
|
var() class<DamageType> damtype;
|
|
var() bool bSever;
|
|
var() Rotator rotDir;
|
|
};
|
|
|
|
var() HitFXData HitFx[8];
|
|
var transient int HitFxTicker;
|
|
|
|
var transient CompressedPosition PawnPosition;
|
|
var Controller DelayedDamageInstigatorController;
|
|
var Controller LastHitBy; //give kill credit to this guy if hit momentum causes pawn to fall to his death
|
|
var float MinFlySpeed;
|
|
var float MaxRotation; // minimum dot product of movement direction and rotation vector, ignored if 0
|
|
|
|
// if _RO_
|
|
|
|
// Physics related flags.
|
|
var bool bWantsToProne; // if true prone (physics will automatically reduce collision height to ProneHeight && ProneRadius)
|
|
var const bool bIsCrawling; // set by physics to specify that pawn is currently crawling
|
|
var() bool bCanProne; // if true, this pawn is capable of proning
|
|
var bool bWasCrawling; // Animation flag used by native phyics anims
|
|
|
|
var bool bIronSights; // Only applies to player anim swapping
|
|
var bool bWasIronSights; // Only applies to player anim swapping
|
|
var bool bMeleeHolding; // Player is pulled back ready to perform a melee strike. Used for native anim code
|
|
var bool bWasMeleeHolding; // Only applies to player anim swapping
|
|
var bool bExplosiveHolding; // Player is pulled back ready to throw an explosive. Used for native anim code
|
|
var bool bWasExplosiveHolding; // Only applies to player anim swapping
|
|
var bool bIsSprinting; // Is the player currently sprinting?
|
|
var bool bIsLimping; // Player was shot in the leg and is limping
|
|
var bool bCanStartSprint; // The player has enough stamina to start sprinting. Set serverside and replicated to clients
|
|
|
|
// Deployment system
|
|
var const bool bCanRestWeapon; // The player is in a position where they can rest thier weapon on something
|
|
var const bool bRestingWeapon; // The player's weapon is resting on a ledge or wall
|
|
var const bool bCanBipodDeploy; // The player is in a position where they deploy thier weapon on a bipod
|
|
var bool bBipodDeployed; // Player has thier weapon deployed on a bipod
|
|
var bool bWasBipodDeployed; // Only applies to player anim swapping
|
|
|
|
// Movement
|
|
var() float SprintPct; // Relative speed for sprint movement
|
|
var() float PronePct; // Relative speed for prone movement
|
|
var() float ProneIronPct; // Relative speed for prone movement with iron sights
|
|
var() float CrouchedSprintPct; // pct. of running speed that crouched sprint speed is
|
|
var() float ProneHeight; // Collision size when prone
|
|
var() float ProneRadius; // Collision size when prone
|
|
|
|
/*==========================================
|
|
* Red Orchestra hit detection system
|
|
*=========================================*/
|
|
|
|
// Hit point types
|
|
enum EPawnHitPointType
|
|
{
|
|
PHP_None,
|
|
PHP_Head,
|
|
PHP_Torso,
|
|
PHP_Arm,
|
|
PHP_Leg,
|
|
PHP_Hand,
|
|
PHP_Foot,
|
|
};
|
|
|
|
// Information for each specific hit area
|
|
struct native PawnHitpoint
|
|
{
|
|
var() float PointRadius; // Squared radius of the head of the pawn that is vulnerable to headshots
|
|
var() float PointHeight; // Distance from base of neck to center of head - used for headshot calculation
|
|
var() float PointScale;
|
|
var() name PointBone;
|
|
var() vector PointOffset; // Amount to offset the hitpoint from the bone
|
|
var() float DamageMultiplier; // Amount to scale damage to the player if this point is hit
|
|
var() EPawnHitPointType HitPointType; // What type of hit point this is
|
|
};
|
|
//PawnHitpoint
|
|
var() array<PawnHitpoint> Hitpoints; // An array of possible small points that can be hit. Index zero is always the driver
|
|
|
|
// Bullet whiz vars
|
|
var vector mWhizSoundLocation; // Location to spawn the bullet whiz effect at
|
|
var byte SpawnWhizCount; // When this is incremented will spawn a bullet whiz
|
|
var byte OldSpawnWhizCount; // Saved bullet whiz counter
|
|
var byte LastWhizType; // The last type of bullet whiz. 0 = none, 1 = bullet, 2 = large shell
|
|
var float LastWhippedTime; // Last time this pawn heard a bullet whip. Used to limit the rate of bullet whips heard
|
|
|
|
// Mine volume vars
|
|
var float MineAreaEnterTime; // Last time this pawn was in a mine area
|
|
var float MineAreaWarnTime; // Last time this pawn was warned in a mine area
|
|
|
|
//Resupply
|
|
var float LastResupplyTime; // Last time this client was resupplied. Not replicated directly as Level.Timeseconds is not the same on server and client
|
|
var bool bTouchingResupply; // This pawn is in an ammo resupply zone
|
|
var sound AmmoResupplySound; // sound to play when this pawn is resupplied with ammo
|
|
|
|
var float AnimBlendTime; // Native code calls the AnimBlendTimer event when the amount of time this is set for reaches zero
|
|
// IF _KF_
|
|
var float CustomAmbientRelevancyScale;// Scale the net relevancy check for Pawns playing ambient sounds by this. Used to have a larger ambient sound radius so sound can be heard well, without making pawns relevant at very long distances
|
|
// end _KF_
|
|
|
|
|
|
// End _RO_
|
|
|
|
// if _KF_
|
|
var const float LedgeCheckThreshold; // Used in determining if pawn is going off ledge.
|
|
// end _KF_
|
|
|
|
replication
|
|
{
|
|
// Variables the server should send to the client.
|
|
reliable if( bNetDirty && (Role==ROLE_Authority) )
|
|
bSimulateGravity, bIsCrouched, bIsWalking, PlayerReplicationInfo, AnimAction, HitDamageType, TakeHitLocation,HeadScale, bIsTyping, DrivenVehicle, bSpecialHUD;
|
|
reliable if( bTearOff && bNetDirty && (Role==ROLE_Authority) )
|
|
TearOffMomentum;
|
|
reliable if ( bNetDirty && !bNetOwner && (Role==ROLE_Authority) )
|
|
bSteadyFiring, ViewPitch; // - jjs
|
|
reliable if( bNetDirty && bNetOwner && Role==ROLE_Authority )
|
|
Controller,SelectedItem, GroundSpeed, WaterSpeed, AirSpeed, AccelRate, JumpZ, AirControl, bCanWallDodge, PitchUpLimit, PitchDownLimit;
|
|
reliable if( bNetDirty && Role==ROLE_Authority )
|
|
Health;
|
|
reliable if ( bNetDirty && bNetInitial && Role==ROLE_Authority )
|
|
HealthMax;
|
|
unreliable if ( !bNetOwner && Role==ROLE_Authority )
|
|
PawnPosition;
|
|
|
|
// xPawn replicated properties - moved here to take advantage of native replication
|
|
reliable if (Role==ROLE_Authority)
|
|
ShieldStrength, HitFx, HitFxTicker;
|
|
|
|
// replicated functions sent to server by owning client
|
|
reliable if( Role<ROLE_Authority )
|
|
ServerChangedWeapon, NextItem, ServerNoTranslocator;
|
|
|
|
// if _RO_
|
|
// ROPawn replicated properties - moved here to take advantage of native replication
|
|
reliable if (bNetDirty && Role == ROLE_Authority)
|
|
bIsCrawling, bIronSights, bIsSprinting, bIsLimping, bMeleeHolding, bExplosiveHolding,
|
|
bBipodDeployed;
|
|
|
|
// ROPawn replicated properties - moved here to take advantage of native replication
|
|
reliable if (bNetOwner && bNetDirty && Role == ROLE_Authority)
|
|
bCanRestWeapon, bRestingWeapon, bCanBipodDeploy, bTouchingResupply;
|
|
|
|
// ROPawn replicated properties - moved here to take advantage of native replication
|
|
reliable if (bNetDirty && bNetOwner && Role == ROLE_Authority)
|
|
bCanStartSprint, SpawnWhizCount, mWhizSoundLocation, LastWhizType;
|
|
|
|
// Server to client functions
|
|
reliable if (Role == ROLE_Authority)
|
|
ClientResupplied;
|
|
// end _RO_
|
|
}
|
|
|
|
// if _RO_
|
|
// ROFunctions - put here to avoid casting
|
|
simulated function bool CanProneTransition(){return true;}
|
|
simulated function bool CanCrouchTransition(){return true;}
|
|
simulated event bool IsTransitioningToProne(){return false;}
|
|
simulated event bool IsProneTransitioning(){return false;}
|
|
simulated event HandleWhizSound();
|
|
|
|
// Set the vars so this bullet whiz is replicated to the owning client
|
|
event PawnWhizzed(vector WhizLocation, int WhizType)
|
|
{
|
|
if((Level.TimeSeconds - LastWhippedTime) > (0.15 + FRand() * 0.15))
|
|
{
|
|
LastWhippedTime = Level.TimeSeconds;
|
|
mWhizSoundLocation = WhizLocation;
|
|
SpawnWhizCount++;
|
|
// Spawn the whiz sound for local non network players
|
|
HandleWhizSound();
|
|
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
}
|
|
}
|
|
|
|
// Play ammo resupply sound for ammo resupply areas
|
|
simulated function ClientResupplied()
|
|
{
|
|
PlayOwnedSound(AmmoResupplySound, SLOT_Interact,3.5*TransientSoundVolume,true,10);
|
|
// Set this value locally on the client rather than replicating it since this value
|
|
// will vary greatly between clients and the server and its only used on the client
|
|
// for the hud indicator
|
|
LastResupplyTime = Level.TimeSeconds;
|
|
}
|
|
|
|
// Tear off momentum gets rounded too much, so lets scale it up, and then unscale it when its used
|
|
// so we avoid the replication rounding
|
|
simulated function SetTearOffMomemtum(vector NewMomentum)
|
|
{
|
|
TearOffMomentum = NewMomentum * 1000;
|
|
}
|
|
|
|
simulated function vector GetTearOffMomemtum()
|
|
{
|
|
return TearOffMomentum/1000;
|
|
}
|
|
|
|
// Blend the upper body back to full body animation.
|
|
// Called by the native code when AnimBlendTime counts down to zero
|
|
simulated event AnimBlendTimer()
|
|
{
|
|
AnimBlendToAlpha(1, 0.0, 0.12);
|
|
}
|
|
|
|
// Get how many frames have passed since this pawn entered ragdoll
|
|
simulated native function int GetRagDollFrames();
|
|
// end _RO_
|
|
|
|
static function StaticPrecache(LevelInfo L);
|
|
simulated native function SetViewPitch( int NewPitch );
|
|
simulated native function SetTwistLook( int twist, int look );
|
|
simulated native function int Get4WayDirection( );
|
|
simulated event SetHeadScale(float NewScale);
|
|
|
|
simulated event PostRender2D(Canvas C, float ScreenLocX, float ScreenLocY) // called if bScriptPostRender is true, overrides native team beacon drawing code
|
|
{
|
|
local PlayerController PC;
|
|
|
|
PC = Level.GetLocalPlayerController();
|
|
|
|
if ( (PC != None) && (PC.myHUD != None) )
|
|
PC.myHUD.DrawCustomBeacon(C,self, ScreenLocX, ScreenLocY);
|
|
}
|
|
|
|
native function bool ReachedDestination(Actor Goal);
|
|
native function ForceCrouch();
|
|
|
|
simulated function Weapon GetDemoRecordingWeapon()
|
|
{
|
|
local inventory Inv;
|
|
local int i;
|
|
|
|
for ( Inv=Inventory; Inv!=None; Inv=Inv.Inventory )
|
|
{
|
|
if ( (Weapon(Inv) != None) && (Inv.ThirdPersonActor != None) )
|
|
{
|
|
Weapon = Weapon(Inv);
|
|
PendingWeapon = Weapon;
|
|
Weapon.bSpectated = true;
|
|
break;
|
|
}
|
|
i++;
|
|
if ( i > 500 )
|
|
return None;
|
|
}
|
|
return Weapon;
|
|
}
|
|
|
|
simulated function SetBaseEyeheight()
|
|
{
|
|
if ( !bIsCrouched )
|
|
BaseEyeheight = Default.BaseEyeheight;
|
|
else
|
|
BaseEyeheight = FMin(0.8 * CrouchHeight, CrouchHeight - 10);
|
|
|
|
Eyeheight = BaseEyeheight;
|
|
}
|
|
|
|
function Pawn GetAimTarget()
|
|
{
|
|
return self;
|
|
}
|
|
|
|
function DeactivateSpawnProtection()
|
|
{
|
|
SpawnTime = -100000;
|
|
}
|
|
|
|
function Actor GetPathTo(Actor Dest)
|
|
{
|
|
if ( PlayerController(Controller) == None )
|
|
return Dest;
|
|
|
|
return PlayerController(Controller).GetPathTo(Dest);
|
|
}
|
|
|
|
function PlayerChangedTeam()
|
|
{
|
|
Died( None, class'DamageType', Location );
|
|
}
|
|
|
|
/* Reset()
|
|
reset actor to initial state - used when restarting level without reloading.
|
|
*/
|
|
function Reset()
|
|
{
|
|
if ( (Controller == None) || Controller.bIsPlayer )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.PawnDied(self);
|
|
Destroy();
|
|
}
|
|
else
|
|
Super.Reset();
|
|
}
|
|
|
|
function bool HasWeapon()
|
|
{
|
|
return (Weapon != None);
|
|
}
|
|
|
|
function ChooseFireAt( Actor A )
|
|
{
|
|
Fire(0);
|
|
}
|
|
|
|
function bool StopWeaponFiring()
|
|
{
|
|
if (Weapon != None && Weapon.IsFiring())
|
|
{
|
|
Weapon.ServerStopFire(0);
|
|
Weapon.ServerStopFire(1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
simulated function Fire( optional float F )
|
|
{
|
|
if (Weapon != None)
|
|
{
|
|
Weapon.Fire(F);
|
|
}
|
|
}
|
|
|
|
simulated function AltFire( optional float F )
|
|
{
|
|
if( Weapon!=None )
|
|
Weapon.AltFire(F);
|
|
}
|
|
|
|
function bool RecommendLongRangedAttack()
|
|
{
|
|
if ( Weapon != None )
|
|
return Weapon.RecommendLongRangedAttack();
|
|
return false;
|
|
}
|
|
|
|
function bool CanAttack(Actor Other)
|
|
{
|
|
if ( Weapon == None )
|
|
return false;
|
|
return Weapon.CanAttack(Other);
|
|
}
|
|
|
|
function bool TooCloseToAttack(Actor Other)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function float RefireRate()
|
|
{
|
|
if (Weapon != None)
|
|
return Weapon.RefireRate();
|
|
|
|
return 0;
|
|
}
|
|
|
|
function bool IsFiring()
|
|
{
|
|
if (Weapon != None)
|
|
return Weapon.IsFiring();
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool FireOnRelease()
|
|
{
|
|
if (Weapon != None)
|
|
return Weapon.FireOnRelease();
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool NeedToTurn(vector targ)
|
|
{
|
|
local vector LookDir, AimDir;
|
|
|
|
LookDir = Vector(Rotation);
|
|
LookDir.Z = 0;
|
|
LookDir = Normal(LookDir);
|
|
AimDir = targ - Location;
|
|
AimDir.Z = 0;
|
|
AimDir = Normal(AimDir);
|
|
|
|
return ((LookDir Dot AimDir) < 0.93);
|
|
}
|
|
|
|
function float ModifyThreat(float current, Pawn Threat)
|
|
{
|
|
return current;
|
|
}
|
|
|
|
function DrawHUD(Canvas Canvas);
|
|
simulated function SpecialDrawCrosshair( Canvas C );
|
|
|
|
// If returns false, do normal calcview anyway
|
|
function bool SpecialCalcView(out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation);
|
|
function bool SpectatorSpecialCalcView(PlayerController Viewer, out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation);
|
|
|
|
simulated function String GetHumanReadableName()
|
|
{
|
|
if ( PlayerReplicationInfo != None )
|
|
return PlayerReplicationInfo.PlayerName;
|
|
return MenuName;
|
|
}
|
|
|
|
function PlayTeleportEffect(bool bOut, bool bSound)
|
|
{
|
|
MakeNoise(1.0);
|
|
}
|
|
|
|
function HoldFlag(Actor FlagActor);
|
|
function DropFlag();
|
|
|
|
function bool PerformDodge(eDoubleClickDir DoubleClickMove, vector Dir, vector Cross);
|
|
|
|
function NotifyTeamChanged();
|
|
|
|
/* PossessedBy()
|
|
Pawn is possessed by Controller
|
|
*/
|
|
function PossessedBy(Controller C)
|
|
{
|
|
Controller = C;
|
|
NetPriority = 3;
|
|
NetUpdateFrequency = 100;
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
if ( C.PlayerReplicationInfo != None )
|
|
{
|
|
PlayerReplicationInfo = C.PlayerReplicationInfo;
|
|
OwnerName = PlayerReplicationInfo.PlayerName;
|
|
}
|
|
if ( C.IsA('PlayerController') )
|
|
{
|
|
if ( bSetPCRotOnPossess )
|
|
C.SetRotation(Rotation);
|
|
if ( Level.NetMode != NM_Standalone )
|
|
RemoteRole = ROLE_AutonomousProxy;
|
|
BecomeViewTarget();
|
|
}
|
|
else
|
|
RemoteRole = Default.RemoteRole;
|
|
|
|
SetOwner(Controller); // for network replication
|
|
Eyeheight = BaseEyeHeight;
|
|
ChangeAnimation();
|
|
}
|
|
|
|
function UnPossessed()
|
|
{
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
if ( DrivenVehicle != None )
|
|
NetUpdateFrequency = 5;
|
|
|
|
PlayerReplicationInfo = None;
|
|
SetOwner(None);
|
|
Controller = None;
|
|
}
|
|
|
|
/* PointOfView()
|
|
called by controller when possessing this pawn
|
|
false = 1st person, true = 3rd person
|
|
*/
|
|
simulated function bool PointOfView()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function BecomeViewTarget()
|
|
{
|
|
bUpdateEyeHeight = true;
|
|
}
|
|
|
|
function DropToGround()
|
|
{
|
|
bCollideWorld = True;
|
|
bInterpolating = false;
|
|
if ( Health > 0 )
|
|
{
|
|
SetCollision(true,true);
|
|
SetPhysics(PHYS_Falling);
|
|
AmbientSound = None;
|
|
if ( IsHumanControlled() )
|
|
Controller.GotoState(LandMovementState);
|
|
}
|
|
}
|
|
|
|
function bool CanGrabLadder()
|
|
{
|
|
return ( bCanClimbLadders
|
|
&& (Controller != None)
|
|
&& (Physics != PHYS_Ladder)
|
|
&& ((Physics != Phys_Falling) || (abs(Velocity.Z) <= JumpZ)) );
|
|
}
|
|
|
|
event SetWalking(bool bNewIsWalking)
|
|
{
|
|
if ( bNewIsWalking != bIsWalking )
|
|
{
|
|
bIsWalking = bNewIsWalking;
|
|
ChangeAnimation();
|
|
}
|
|
}
|
|
|
|
simulated function bool CanSplash()
|
|
{
|
|
if ( (Level.TimeSeconds - SplashTime > 0.15)
|
|
&& ((Physics == PHYS_Falling) || (Physics == PHYS_Flying))
|
|
&& (Abs(Velocity.Z) > 100) )
|
|
{
|
|
SplashTime = Level.TimeSeconds;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function EndClimbLadder(LadderVolume OldLadder)
|
|
{
|
|
if ( Controller != None )
|
|
Controller.EndClimbLadder();
|
|
if ( Physics == PHYS_Ladder )
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
|
|
function ClimbLadder(LadderVolume L)
|
|
{
|
|
OnLadder = L;
|
|
SetRotation(OnLadder.WallDir);
|
|
SetPhysics(PHYS_Ladder);
|
|
if ( IsHumanControlled() )
|
|
Controller.GotoState('PlayerClimbing');
|
|
}
|
|
|
|
/* DisplayDebug()
|
|
list important actor variable on canvas. Also show the pawn's controller and weapon info
|
|
*/
|
|
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
|
|
{
|
|
local string T;
|
|
Super.DisplayDebug(Canvas, YL, YPos);
|
|
|
|
Canvas.SetDrawColor(255,255,255);
|
|
|
|
Canvas.DrawText("Animation Action "$AnimAction$" Health "$Health);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
Canvas.DrawText("Anchor "$Anchor$" Serpentine Dist "$SerpentineDist$" Time "$SerpentineTime);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
|
|
T = "Floor "$Floor$" DesiredSpeed "$DesiredSpeed$" Crouched "$bIsCrouched$" Try to uncrouch "$UncrouchTime;
|
|
if ( (OnLadder != None) || (Physics == PHYS_Ladder) )
|
|
T=T$" on ladder "$OnLadder;
|
|
Canvas.DrawText(T);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
Canvas.DrawText("EyeHeight "$Eyeheight$" BaseEyeHeight "$BaseEyeHeight$" Physics Anim "$bPhysicsAnimUpdate);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
|
|
if ( Controller == None )
|
|
{
|
|
Canvas.SetDrawColor(255,0,0);
|
|
Canvas.DrawText("NO CONTROLLER");
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
else
|
|
{
|
|
if ( Controller.PlayerReplicationInfo != None )
|
|
{
|
|
Canvas.SetDrawColor(255,0,0);
|
|
Canvas.DrawText("Owned by "$Controller.PlayerReplicationInfo.PlayerName);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
Controller.DisplayDebug(Canvas,YL,YPos);
|
|
}
|
|
if ( Weapon == None )
|
|
{
|
|
Canvas.SetDrawColor(0,255,0);
|
|
Canvas.DrawText("NO WEAPON");
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
else
|
|
Weapon.DisplayDebug(Canvas,YL,YPos);
|
|
}
|
|
|
|
//
|
|
// Compute offset for drawing an inventory item.
|
|
//
|
|
simulated function vector CalcDrawOffset(inventory Inv)
|
|
{
|
|
local vector DrawOffset;
|
|
|
|
if ( Controller == None )
|
|
return (Inv.PlayerViewOffset >> Rotation) + BaseEyeHeight * vect(0,0,1);
|
|
|
|
DrawOffset = ((0.9/Weapon.DisplayFOV * 100 * ModifiedPlayerViewOffset(Inv)) >> GetViewRotation() );
|
|
if ( !IsLocallyControlled() )
|
|
DrawOffset.Z += BaseEyeHeight;
|
|
else
|
|
{
|
|
DrawOffset.Z += EyeHeight;
|
|
if( bWeaponBob )
|
|
DrawOffset += WeaponBob(Inv.BobDamping);
|
|
DrawOffset += CameraShake();
|
|
}
|
|
return DrawOffset;
|
|
}
|
|
|
|
// IF_RO_
|
|
//
|
|
// Compute offset for drawing a weapon in ironsights.
|
|
//
|
|
simulated function vector CalcZoomedDrawOffset(inventory Inv)
|
|
{
|
|
local vector DrawOffset;
|
|
|
|
if ( Controller == None )
|
|
return (Inv.PlayerViewOffset >> Rotation) + BaseEyeHeight * vect(0,0,1);
|
|
|
|
DrawOffset = ((0.9/Weapon.DisplayFOV * 100 * ZoomedModifiedPlayerViewOffset(Inv)));
|
|
if ( IsLocallyControlled() )
|
|
{
|
|
DrawOffset += ZoomedWeaponBob(Inv.BobDamping);
|
|
DrawOffset += ZoomedCameraShake();
|
|
}
|
|
return DrawOffset;
|
|
}
|
|
|
|
// Calculate weapon bob for a weapon in IS (removing player rotation since IS weapons are drawn in hud space)
|
|
simulated function vector ZoomedWeaponBob(float BobDamping)
|
|
{
|
|
Local Vector WBob;
|
|
|
|
WBob = BobDamping * (WalkBob >> (GetViewRotation() * -1));
|
|
WBob.Z = (0.45 + 0.55 * BobDamping) * WalkBob.Z;
|
|
WBob.Z += LandBob;
|
|
return WBob;
|
|
}
|
|
|
|
simulated function vector ZoomedCameraShake()
|
|
{
|
|
local vector shakevect;
|
|
local PlayerController pc;
|
|
|
|
pc = PlayerController(Controller);
|
|
|
|
if (pc == None)
|
|
return shakevect;
|
|
|
|
// Scale the shake down 5x since first person weapons are scaled up 5x
|
|
shakevect = pc.ShakeOffset * 0.2;
|
|
|
|
return shakevect;
|
|
}
|
|
|
|
simulated function vector ZoomedModifiedPlayerViewOffset(inventory Inv)
|
|
{
|
|
return -Inv.PlayerViewOffset;
|
|
}
|
|
// end _RO_
|
|
|
|
simulated function vector CameraShake()
|
|
{
|
|
local vector x, y, z, shakevect;
|
|
local PlayerController pc;
|
|
|
|
pc = PlayerController(Controller);
|
|
|
|
if (pc == None)
|
|
return shakevect;
|
|
|
|
GetAxes(pc.Rotation, x, y, z);
|
|
|
|
shakevect = pc.ShakeOffset.X * x +
|
|
pc.ShakeOffset.Y * y +
|
|
pc.ShakeOffset.Z * z;
|
|
|
|
return shakevect;
|
|
}
|
|
|
|
simulated function vector ModifiedPlayerViewOffset(inventory Inv)
|
|
{
|
|
return Inv.PlayerViewOffset;
|
|
}
|
|
|
|
simulated function vector WeaponBob(float BobDamping)
|
|
{
|
|
Local Vector WBob;
|
|
|
|
WBob = BobDamping * WalkBob;
|
|
WBob.Z = (0.45 + 0.55 * BobDamping) * WalkBob.Z;
|
|
WBob.Z += LandBob;
|
|
return WBob;
|
|
}
|
|
|
|
function CheckBob(float DeltaTime, vector Y)
|
|
{
|
|
local float Speed2D;
|
|
|
|
if( !bWeaponBob || bJustLanded )
|
|
{
|
|
BobTime = 0;
|
|
WalkBob = Vect(0,0,0);
|
|
return;
|
|
}
|
|
Bob = FClamp(Bob, -0.01, 0.01);
|
|
if (Physics == PHYS_Walking )
|
|
{
|
|
Speed2D = VSize(Velocity);
|
|
if ( Speed2D < 10 )
|
|
BobTime += 0.2 * DeltaTime;
|
|
else
|
|
BobTime += DeltaTime * (0.3 + 0.7 * Speed2D/GroundSpeed);
|
|
WalkBob = Y * Bob * Speed2D * sin(8 * BobTime);
|
|
AppliedBob = AppliedBob * (1 - FMin(1, 16 * deltatime));
|
|
WalkBob.Z = AppliedBob;
|
|
if ( Speed2D > 10 )
|
|
WalkBob.Z = WalkBob.Z + 0.75 * Bob * Speed2D * sin(16 * BobTime);
|
|
}
|
|
else if ( Physics == PHYS_Swimming )
|
|
{
|
|
BobTime += DeltaTime;
|
|
Speed2D = Sqrt(Velocity.X * Velocity.X + Velocity.Y * Velocity.Y);
|
|
WalkBob = Y * Bob * 0.5 * Speed2D * sin(4.0 * BobTime);
|
|
WalkBob.Z = Bob * 1.5 * Speed2D * sin(8.0 * BobTime);
|
|
}
|
|
else
|
|
{
|
|
BobTime = 0;
|
|
WalkBob = WalkBob * (1 - FMin(1, 8 * deltatime));
|
|
}
|
|
}
|
|
|
|
//***************************************
|
|
// Vehicle driving
|
|
// StartDriving() and StopDriving() also called on client
|
|
// on transitions of bIsDriving setting
|
|
|
|
simulated event StartDriving(Vehicle V)
|
|
{
|
|
DrivenVehicle = V;
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
AmbientSound = None;
|
|
StopWeaponFiring();
|
|
DeactivateSpawnProtection();
|
|
|
|
// Move the driver into position, and attach to car.
|
|
ShouldCrouch(false);
|
|
bIgnoreForces = true;
|
|
Velocity = vect(0,0,0);
|
|
Acceleration = vect(0,0,0);
|
|
bCanTeleport = false;
|
|
|
|
if ( !V.bRemoteControlled || V.bHideRemoteDriver )
|
|
{
|
|
SetCollision( False, False, False);
|
|
bCollideWorld = false;
|
|
V.AttachDriver( Self );
|
|
if ( V.bDrawDriverinTP )
|
|
CullDistance = 5000;
|
|
else
|
|
bHidden = true;
|
|
}
|
|
|
|
// set animation
|
|
bPhysicsAnimUpdate = false;
|
|
bWaitForAnim = false;
|
|
if ( !V.bHideRemoteDriver && V.bDrawDriverinTP )
|
|
{
|
|
if ( HasAnim(DrivenVehicle.DriveAnim) )
|
|
LoopAnim(DrivenVehicle.DriveAnim);
|
|
else
|
|
LoopAnim('Vehicle_Driving');
|
|
SetAnimFrame(0.5);
|
|
SmoothViewYaw = Rotation.Yaw;
|
|
SetTwistLook(0,0);
|
|
}
|
|
else if ( !V.bRemoteControlled )
|
|
{
|
|
LoopAnim('Vehicle_Driving');
|
|
}
|
|
}
|
|
|
|
simulated event StopDriving(Vehicle V)
|
|
{
|
|
if ( (Role == ROLE_Authority) && (PlayerController(Controller) != None) )
|
|
V.PlayerStartTime = Level.TimeSeconds + 12;
|
|
CullDistance = Default.CullDistance;
|
|
NetUpdateTime = Level.TimeSeconds - 1;
|
|
|
|
if (V != None && V.Weapon != None )
|
|
V.Weapon.ImmediateStopFire();
|
|
|
|
if ( Physics == PHYS_Karma )
|
|
return;
|
|
|
|
DrivenVehicle = None;
|
|
bIgnoreForces = false;
|
|
bHardAttach = false;
|
|
bWaitForAnim = false;
|
|
bCanTeleport = true;
|
|
bCollideWorld = true;
|
|
PlayWaiting();
|
|
//PrePivot = vect(0,0,0);
|
|
|
|
if ( V != None )
|
|
V.DetachDriver( Self );
|
|
|
|
bPhysicsAnimUpdate = Default.bPhysicsAnimUpdate;
|
|
SetCollision(True, True);
|
|
|
|
if ( (Role == ROLE_Authority) && (Health > 0) )
|
|
{
|
|
if ( !V.bRemoteControlled || V.bHideRemoteDriver )
|
|
{
|
|
Acceleration = vect(0, 0, 24000);
|
|
if ( PhysicsVolume.bWaterVolume )
|
|
SetPhysics(PHYS_Swimming);
|
|
else
|
|
SetPhysics(PHYS_Falling);
|
|
SetBase(None);
|
|
bHidden = false;
|
|
}
|
|
}
|
|
|
|
bOwnerNoSee = default.bOwnerNoSee;
|
|
|
|
if ( Weapon != None )
|
|
{
|
|
PendingWeapon = None;
|
|
Weapon.BringUp();
|
|
}
|
|
}
|
|
|
|
simulated function bool FindValidTaunt( out name Sequence )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// CheckTauntValid() is obsolete, use FindValidTaunt()
|
|
simulated function bool CheckTauntValid( name Sequence )
|
|
{
|
|
return FindValidTaunt(sequence);
|
|
}
|
|
|
|
//***************************************
|
|
// Interface to Pawn's Controller
|
|
|
|
// return true if controlled by a Player (AI or human)
|
|
simulated function bool IsPlayerPawn()
|
|
{
|
|
return ( (Controller != None) && Controller.bIsPlayer );
|
|
}
|
|
|
|
// return true if was controlled by a Player (AI or human)
|
|
simulated function bool WasPlayerPawn()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// return true if controlled by a real live human
|
|
simulated function bool IsHumanControlled()
|
|
{
|
|
return ( PlayerController(Controller) != None );
|
|
}
|
|
|
|
// return true if controlled by local (not network) player
|
|
simulated function bool IsLocallyControlled()
|
|
{
|
|
if ( Level.NetMode == NM_Standalone )
|
|
return true;
|
|
if ( Controller == None )
|
|
return false;
|
|
if ( PlayerController(Controller) == None )
|
|
return true;
|
|
|
|
return ( Viewport(PlayerController(Controller).Player) != None );
|
|
}
|
|
|
|
// return true if viewing this pawn in first person pov. useful for determining what and where to spawn effects
|
|
simulated function bool IsFirstPerson()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
PC = PlayerController(Controller);
|
|
return ( PC != None && !PC.bBehindView && Viewport(PC.Player) != None );
|
|
}
|
|
|
|
simulated function rotator GetViewRotation()
|
|
{
|
|
if ( Controller == None )
|
|
return Rotation;
|
|
return Controller.GetViewRotation();
|
|
}
|
|
|
|
simulated function SetViewRotation(rotator NewRotation )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.SetRotation(NewRotation);
|
|
}
|
|
|
|
final function bool InGodMode()
|
|
{
|
|
return ( (Controller != None) && Controller.bGodMode );
|
|
}
|
|
|
|
function bool NearMoveTarget()
|
|
{
|
|
if ( (Controller == None) || (Controller.MoveTarget == None) )
|
|
return false;
|
|
|
|
return ReachedDestination(Controller.MoveTarget);
|
|
}
|
|
|
|
simulated final function bool PressingFire()
|
|
{
|
|
return ( (Controller != None) && (Controller.bFire != 0) );
|
|
}
|
|
|
|
simulated final function bool PressingAltFire()
|
|
{
|
|
return ( (Controller != None) && (Controller.bAltFire != 0) );
|
|
}
|
|
|
|
function Actor GetMoveTarget()
|
|
{
|
|
if ( Controller == None )
|
|
return None;
|
|
|
|
return Controller.MoveTarget;
|
|
}
|
|
|
|
function SetMoveTarget(Actor NewTarget )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.MoveTarget = NewTarget;
|
|
}
|
|
|
|
function bool LineOfSightTo(actor Other)
|
|
{
|
|
return ( (Controller != None) && Controller.LineOfSightTo(Other) );
|
|
}
|
|
|
|
simulated final function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
|
|
{
|
|
if ( Controller == None )
|
|
return Rotation;
|
|
|
|
return Controller.AdjustAim(FiredAmmunition, projStart, aimerror);
|
|
}
|
|
|
|
function Actor ShootSpecial(Actor A)
|
|
{
|
|
if ( !Controller.bCanDoSpecial || (Weapon == None) )
|
|
return None;
|
|
|
|
SetRotation(rotator(A.Location - Location));
|
|
Controller.Focus = A;
|
|
Controller.FireWeaponAt(A);
|
|
return A;
|
|
}
|
|
|
|
/* return a value (typically 0 to 1) adjusting pawn's perceived strength if under some special influence (like berserk)
|
|
*/
|
|
function float AdjustedStrength()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
function HandlePickup(Pickup pick)
|
|
{
|
|
MakeNoise(0.2);
|
|
if ( Controller != None )
|
|
Controller.HandlePickup(pick);
|
|
}
|
|
|
|
function ReceiveLocalizedMessage( class<LocalMessage> Message, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject )
|
|
{
|
|
if ( PlayerController(Controller) != None )
|
|
PlayerController(Controller).ReceiveLocalizedMessage( Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
|
|
}
|
|
|
|
event ClientMessage( coerce string S, optional Name Type )
|
|
{
|
|
if ( PlayerController(Controller) != None )
|
|
PlayerController(Controller).ClientMessage( S, Type );
|
|
}
|
|
|
|
function Trigger( actor Other, pawn EventInstigator )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.Trigger(Other, EventInstigator);
|
|
}
|
|
|
|
//***************************************
|
|
|
|
function bool CanTrigger(Trigger T)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
function CreateInventory(string InventoryClassName)
|
|
{
|
|
}
|
|
|
|
function GiveWeapon(string aClassName )
|
|
{
|
|
local class<Weapon> WeaponClass;
|
|
local Weapon NewWeapon;
|
|
|
|
WeaponClass = class<Weapon>(DynamicLoadObject(aClassName, class'Class'));
|
|
|
|
if( FindInventoryType(WeaponClass) != None )
|
|
return;
|
|
newWeapon = Spawn(WeaponClass);
|
|
if( newWeapon != None )
|
|
newWeapon.GiveTo(self);
|
|
}
|
|
|
|
function SetDisplayProperties(ERenderStyle NewStyle, Material NewTexture, bool bLighting )
|
|
{
|
|
Style = NewStyle;
|
|
Texture = NewTexture;
|
|
bUnlit = bLighting;
|
|
if ( Weapon != None )
|
|
Weapon.SetDisplayProperties(Style, Texture, bUnlit);
|
|
|
|
if ( !bUpdatingDisplay && (Inventory != None) )
|
|
{
|
|
bUpdatingDisplay = true;
|
|
Inventory.SetOwnerDisplay();
|
|
}
|
|
bUpdatingDisplay = false;
|
|
}
|
|
|
|
function SetDefaultDisplayProperties()
|
|
{
|
|
Style = Default.Style;
|
|
texture = Default.Texture;
|
|
bUnlit = Default.bUnlit;
|
|
if ( Weapon != None )
|
|
Weapon.SetDefaultDisplayProperties();
|
|
|
|
if ( !bUpdatingDisplay && (Inventory != None) )
|
|
{
|
|
bUpdatingDisplay = true;
|
|
Inventory.SetOwnerDisplay();
|
|
}
|
|
bUpdatingDisplay = false;
|
|
}
|
|
|
|
function FinishedInterpolation()
|
|
{
|
|
DropToGround();
|
|
}
|
|
|
|
function JumpOutOfWater(vector jumpDir)
|
|
{
|
|
Falling();
|
|
Velocity = jumpDir * WaterSpeed;
|
|
Acceleration = jumpDir * AccelRate;
|
|
velocity.Z = FMax(380,JumpZ); //set here so physics uses this for remainder of tick
|
|
bUpAndOut = true;
|
|
}
|
|
|
|
/*
|
|
Modify velocity called by physics before applying new velocity
|
|
for this tick.
|
|
|
|
Velocity,Acceleration, etc. have been updated by the physics, but location hasn't
|
|
*/
|
|
simulated event ModifyVelocity(float DeltaTime, vector OldVelocity);
|
|
|
|
event FellOutOfWorld(eKillZType KillType)
|
|
{
|
|
if ( Level.NetMode == NM_Client )
|
|
return;
|
|
if ( (Controller != None) && Controller.AvoidCertainDeath() )
|
|
return;
|
|
|
|
Health = -1;
|
|
|
|
if( KillType == KILLZ_Lava)
|
|
Died( None, class'FellLava', Location );
|
|
else if(KillType == KILLZ_Suicide)
|
|
Died( None, class'Fell', Location );
|
|
else
|
|
{
|
|
if ( Physics != PHYS_Karma )
|
|
SetPhysics(PHYS_None);
|
|
Died( None, class'Fell', Location );
|
|
}
|
|
}
|
|
|
|
/* ShouldCrouch()
|
|
Controller is requesting that pawn crouch
|
|
*/
|
|
function ShouldCrouch(bool Crouch)
|
|
{
|
|
if ( bWantsToCrouch != Crouch )
|
|
{
|
|
bWantsToCrouch = Crouch;
|
|
|
|
// Have to unpress crawl so you don't automatically go to crouch when you
|
|
// stand uncrouch if toggle crouch is on
|
|
if( bWantsToCrouch )
|
|
{
|
|
if( Controller != none )
|
|
{
|
|
Controller.bCrawl = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if _RO_
|
|
/* ShouldProne()
|
|
Controller is requesting that pawn prone
|
|
*/
|
|
function ShouldProne(bool Prone)
|
|
{
|
|
if ( bWantsToProne != Prone )
|
|
{
|
|
bWantsToProne = Prone;
|
|
|
|
// Have to unpress duck so you don't automatically go to crouch when you
|
|
// unprone if toggle duck is on
|
|
if( bWantsToProne )
|
|
{
|
|
if( Controller != none )
|
|
{
|
|
Controller.bDuck = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// end if _RO_
|
|
|
|
// Stub events called when physics actually allows crouch to begin or end
|
|
// use these for changing the animation (if script controlled)
|
|
event EndCrouch(float HeightAdjust)
|
|
{
|
|
EyeHeight -= HeightAdjust;
|
|
OldZ += HeightAdjust;
|
|
BaseEyeHeight = Default.BaseEyeHeight;
|
|
}
|
|
|
|
event StartCrouch(float HeightAdjust)
|
|
{
|
|
EyeHeight += HeightAdjust;
|
|
OldZ -= HeightAdjust;
|
|
BaseEyeHeight = FMin(0.8 * CrouchHeight, CrouchHeight - 10);
|
|
}
|
|
|
|
function RestartPlayer();
|
|
function AddVelocity( vector NewVelocity)
|
|
{
|
|
if ( bIgnoreForces || (NewVelocity == vect(0,0,0)) )
|
|
return;
|
|
if ( (Physics == PHYS_Falling) && (AIController(Controller) != None) )
|
|
ImpactVelocity += NewVelocity;
|
|
if ( (Physics == PHYS_Walking)
|
|
|| (((Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) && (NewVelocity.Z > Default.JumpZ)) )
|
|
SetPhysics(PHYS_Falling);
|
|
if ( (Velocity.Z > 380) && (NewVelocity.Z > 0) )
|
|
NewVelocity.Z *= 0.5;
|
|
Velocity += NewVelocity;
|
|
}
|
|
|
|
function KilledBy( pawn EventInstigator )
|
|
{
|
|
local Controller Killer;
|
|
|
|
Health = 0;
|
|
if ( EventInstigator != None )
|
|
Killer = EventInstigator.Controller;
|
|
Died( Killer, class'Suicided', Location );
|
|
}
|
|
|
|
function TakeFallingDamage()
|
|
{
|
|
local float Shake, EffectiveSpeed;
|
|
|
|
if (Velocity.Z < -0.5 * MaxFallSpeed)
|
|
{
|
|
if ( Role == ROLE_Authority )
|
|
{
|
|
MakeNoise(1.0);
|
|
if (Velocity.Z < -1 * MaxFallSpeed)
|
|
{
|
|
EffectiveSpeed = Velocity.Z;
|
|
if ( TouchingWaterVolume() )
|
|
EffectiveSpeed = FMin(0, EffectiveSpeed + 100);
|
|
if ( EffectiveSpeed < -1 * MaxFallSpeed )
|
|
TakeDamage(-100 * (EffectiveSpeed + MaxFallSpeed)/MaxFallSpeed, None, Location, vect(0,0,0), class'Fell');
|
|
}
|
|
}
|
|
if ( Controller != None )
|
|
{
|
|
Shake = FMin(1, -1 * Velocity.Z/MaxFallSpeed);
|
|
Controller.DamageShake(Shake);
|
|
}
|
|
}
|
|
else if (Velocity.Z < -1.4 * JumpZ)
|
|
MakeNoise(0.5);
|
|
}
|
|
|
|
function ClientReStart()
|
|
{
|
|
Velocity = vect(0,0,0);
|
|
Acceleration = vect(0,0,0);
|
|
BaseEyeHeight = Default.BaseEyeHeight;
|
|
EyeHeight = BaseEyeHeight;
|
|
PlayWaiting();
|
|
}
|
|
|
|
function ClientSetLocation( vector NewLocation, rotator NewRotation )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.ClientSetLocation(NewLocation, NewRotation);
|
|
}
|
|
|
|
function ClientSetRotation( rotator NewRotation )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.ClientSetRotation(NewRotation);
|
|
}
|
|
|
|
simulated function FaceRotation( rotator NewRotation, float DeltaTime )
|
|
{
|
|
if ( Physics == PHYS_Ladder )
|
|
SetRotation(OnLadder.Walldir);
|
|
else
|
|
{
|
|
if ( (Physics == PHYS_Walking) || (Physics == PHYS_Falling) )
|
|
NewRotation.Pitch = 0;
|
|
SetRotation(NewRotation);
|
|
}
|
|
}
|
|
|
|
// if _RO_
|
|
// Added deltatime here so we could do some smooth pitch interpolations
|
|
function int LimitPitch(int pitch, optional float DeltaTime)
|
|
// else
|
|
//function int LimitPitch(int pitch)
|
|
{
|
|
pitch = pitch & 65535;
|
|
|
|
if (pitch > PitchUpLimit && pitch < PitchDownLimit)
|
|
{
|
|
if (pitch - PitchUpLimit < PitchDownLimit - pitch)
|
|
pitch = PitchUpLimit;
|
|
else
|
|
pitch = PitchDownLimit;
|
|
}
|
|
|
|
return pitch;
|
|
}
|
|
|
|
function ClientDying(class<DamageType> DamageType, vector HitLocation)
|
|
{
|
|
if ( Controller != None )
|
|
Controller.ClientDying(DamageType, HitLocation);
|
|
}
|
|
|
|
function DoComboName( string ComboClassName );
|
|
function bool InCurrentCombo()
|
|
{
|
|
return false;
|
|
}
|
|
//=============================================================================
|
|
// UDamage stub.
|
|
function EnableUDamage(float Amount);
|
|
function DisableUDamage();
|
|
|
|
//=============================================================================
|
|
// Shield stubs.
|
|
function float GetShieldStrengthMax();
|
|
function float GetShieldStrength();
|
|
function bool AddShieldStrength(int Amount);
|
|
function int CanUseShield(int Amount);
|
|
|
|
//=============================================================================
|
|
// Inventory related functions.
|
|
|
|
// check before throwing
|
|
simulated function bool CanThrowWeapon()
|
|
{
|
|
return ( (Weapon != None) && Weapon.CanThrow() && Level.Game.bAllowWeaponThrowing );
|
|
}
|
|
|
|
// toss out a weapon
|
|
function TossWeapon(Vector TossVel)
|
|
{
|
|
local Vector X,Y,Z;
|
|
|
|
Weapon.Velocity = TossVel;
|
|
GetAxes(Rotation,X,Y,Z);
|
|
Weapon.DropFrom(Location + 0.8 * CollisionRadius * X - 0.5 * CollisionRadius * Y);
|
|
}
|
|
|
|
exec function SwitchToLastWeapon()
|
|
{
|
|
if ( (Weapon != None) && (Weapon.OldWeapon != None) && Weapon.OldWeapon.HasAmmo() )
|
|
{
|
|
PendingWeapon = Weapon.OldWeapon;
|
|
Weapon.PutDown();
|
|
}
|
|
}
|
|
|
|
/* PrevWeapon()
|
|
- switch to previous inventory group weapon
|
|
*/
|
|
simulated function PrevWeapon()
|
|
{
|
|
if ( Level.Pauser != None )
|
|
return;
|
|
|
|
if ( Weapon == None && Controller != None )
|
|
{
|
|
Controller.SwitchToBestWeapon();
|
|
return;
|
|
}
|
|
if ( PendingWeapon != None )
|
|
{
|
|
if ( PendingWeapon.bForceSwitch )
|
|
return;
|
|
PendingWeapon = Inventory.PrevWeapon(None, PendingWeapon);
|
|
}
|
|
else
|
|
PendingWeapon = Inventory.PrevWeapon(None, Weapon);
|
|
|
|
if ( PendingWeapon != None )
|
|
Weapon.PutDown();
|
|
}
|
|
|
|
/* NextWeapon()
|
|
- switch to next inventory group weapon
|
|
*/
|
|
simulated function NextWeapon()
|
|
{
|
|
if ( Level.Pauser != None )
|
|
return;
|
|
|
|
if ( Weapon == None && Controller != None )
|
|
{
|
|
Controller.SwitchToBestWeapon();
|
|
return;
|
|
}
|
|
if ( PendingWeapon != None )
|
|
{
|
|
if ( PendingWeapon.bForceSwitch )
|
|
return;
|
|
PendingWeapon = Inventory.NextWeapon(None, PendingWeapon);
|
|
}
|
|
else
|
|
PendingWeapon = Inventory.NextWeapon(None, Weapon);
|
|
|
|
if ( PendingWeapon != None )
|
|
Weapon.PutDown();
|
|
}
|
|
|
|
|
|
// The player wants to switch to weapon group number F.
|
|
simulated function SwitchWeapon(byte F)
|
|
{
|
|
local weapon newWeapon;
|
|
|
|
if ( (Level.Pauser!=None) || (Inventory == None) )
|
|
return;
|
|
if ( (Weapon != None) && (Weapon.Inventory != None) )
|
|
newWeapon = Weapon.Inventory.WeaponChange(F, false);
|
|
else
|
|
newWeapon = None;
|
|
if ( newWeapon == None )
|
|
newWeapon = Inventory.WeaponChange(F, true);
|
|
|
|
if ( newWeapon == None )
|
|
{
|
|
if ( F == 10 )
|
|
ServerNoTranslocator();
|
|
|
|
return;
|
|
}
|
|
|
|
if ( PendingWeapon != None && PendingWeapon.bForceSwitch )
|
|
return;
|
|
|
|
if ( Weapon == None )
|
|
{
|
|
PendingWeapon = newWeapon;
|
|
ChangedWeapon();
|
|
}
|
|
else if ( Weapon != newWeapon || PendingWeapon != None )
|
|
{
|
|
PendingWeapon = newWeapon;
|
|
Weapon.PutDown();
|
|
}
|
|
else if ( Weapon == newWeapon )
|
|
Weapon.Reselect(); // sjs
|
|
}
|
|
|
|
function ServerNoTranslocator()
|
|
{
|
|
if ( Level.Game != None )
|
|
Level.Game.NoTranslocatorKeyPressed( PlayerController(Controller) );
|
|
}
|
|
|
|
// The player/bot wants to select next item
|
|
exec function NextItem()
|
|
{
|
|
if (SelectedItem==None) {
|
|
SelectedItem = Inventory.SelectNext();
|
|
Return;
|
|
}
|
|
if (SelectedItem.Inventory!=None)
|
|
SelectedItem = SelectedItem.Inventory.SelectNext();
|
|
else
|
|
SelectedItem = Inventory.SelectNext();
|
|
|
|
if ( SelectedItem == None )
|
|
SelectedItem = Inventory.SelectNext();
|
|
}
|
|
|
|
// FindInventoryType()
|
|
// returns the inventory item of the requested class
|
|
// if it exists in this pawn's inventory
|
|
|
|
function Inventory FindInventoryType( class DesiredClass )
|
|
{
|
|
local Inventory Inv;
|
|
local int Count;
|
|
|
|
for( Inv=Inventory; Inv!=None && Count < 1000; Inv=Inv.Inventory )
|
|
{
|
|
if ( Inv.class == DesiredClass )
|
|
return Inv;
|
|
Count++;
|
|
}
|
|
|
|
// Search for subclasses if exact class wasn't found
|
|
Count = 0;
|
|
for ( Inv = Inventory; Inv != None && Count < 1000; Inv = Inv.Inventory )
|
|
{
|
|
if ( ClassIsChildOf(Inv.Class, DesiredClass) )
|
|
return Inv;
|
|
Count++;
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
// Add Item to this pawn's inventory.
|
|
// Returns true if successfully added, false if not.
|
|
function bool AddInventory( inventory NewItem )
|
|
{
|
|
// Skip if already in the inventory.
|
|
local inventory Inv;
|
|
local actor Last, Prev;
|
|
local bool bAddedInOrder;
|
|
|
|
Last = self;
|
|
|
|
// The item should not have been destroyed if we get here.
|
|
if (NewItem ==None )
|
|
log("tried to add none inventory to "$self);
|
|
|
|
NewItem.SetOwner(Self);
|
|
NewItem.NetUpdateTime = Level.TimeSeconds - 1;
|
|
|
|
// order weapons based on priority
|
|
if ( Weapon(NewItem) != None )
|
|
{
|
|
Prev = self;
|
|
for( Inv=Inventory; Inv!=None; Inv=Inv.Inventory )
|
|
{
|
|
if ( (Inv.InventoryGroup == NewItem.InventoryGroup) && (Weapon(Inv) != None) )
|
|
{
|
|
if( (Weapon(Inv).Priority < Weapon(NewItem).Priority) )
|
|
{
|
|
bAddedInOrder = true;
|
|
break;
|
|
}
|
|
}
|
|
else if ( (Weapon(Prev) != None) && (Weapon(Prev).InventoryGroup == NewItem.InventoryGroup) )
|
|
{
|
|
bAddedInOrder = true;
|
|
break;
|
|
}
|
|
if ( !bAddedInOrder )
|
|
Prev = Inv;
|
|
}
|
|
if ( bAddedInOrder )
|
|
{
|
|
NewItem.Inventory = Prev.Inventory;
|
|
Prev.Inventory = NewItem;
|
|
Prev.NetUpdateTime = Level.TimeSeconds - 1;
|
|
}
|
|
}
|
|
|
|
if ( !bAddedInOrder )
|
|
{
|
|
for( Inv=Inventory; Inv!=None; Inv=Inv.Inventory )
|
|
{
|
|
if( Inv == NewItem )
|
|
return false;
|
|
Last = Inv;
|
|
}
|
|
|
|
// Add to back of inventory chain (so minimizes net replication effect).
|
|
NewItem.Inventory = None;
|
|
Last.Inventory = NewItem;
|
|
Last.NetUpdateTime = Level.TimeSeconds - 1;
|
|
}
|
|
if ( Controller != None )
|
|
Controller.NotifyAddInventory(NewItem);
|
|
return true;
|
|
}
|
|
|
|
// Remove Item from this pawn's inventory, if it exists.
|
|
function DeleteInventory( inventory Item )
|
|
{
|
|
// If this item is in our inventory chain, unlink it.
|
|
local actor Link;
|
|
local int Count;
|
|
|
|
if ( Item == Weapon )
|
|
Weapon = None;
|
|
if ( Item == SelectedItem )
|
|
SelectedItem = None;
|
|
for( Link = Self; Link!=None; Link=Link.Inventory )
|
|
{
|
|
if( Link.Inventory == Item )
|
|
{
|
|
Link.Inventory = Item.Inventory;
|
|
Item.Inventory = None;
|
|
Link.NetUpdateTime = Level.TimeSeconds - 1;
|
|
Item.NetUpdateTime = Level.TimeSeconds - 1;
|
|
break;
|
|
}
|
|
if ( Level.NetMode == NM_Client )
|
|
{
|
|
Count++;
|
|
if ( Count > 1000 )
|
|
break;
|
|
}
|
|
}
|
|
Item.SetOwner(None);
|
|
}
|
|
|
|
// Just changed to pendingWeapon
|
|
simulated function ChangedWeapon()
|
|
{
|
|
local Weapon OldWeapon;
|
|
|
|
ServerChangedWeapon(Weapon, PendingWeapon);
|
|
if (Role < ROLE_Authority)
|
|
{
|
|
OldWeapon = Weapon;
|
|
Weapon = PendingWeapon;
|
|
PendingWeapon = None;
|
|
if ( Controller != None )
|
|
Controller.ChangedWeapon();
|
|
|
|
if (Weapon != None)
|
|
Weapon.BringUp(OldWeapon);
|
|
}
|
|
}
|
|
|
|
function name GetOffhandBoneFor(Inventory I)
|
|
{
|
|
return '';
|
|
}
|
|
|
|
function name GetWeaponBoneFor(Inventory I)
|
|
{
|
|
return 'righthand';
|
|
}
|
|
|
|
function ServerChangedWeapon(Weapon OldWeapon, Weapon NewWeapon)
|
|
{
|
|
Weapon = NewWeapon;
|
|
|
|
if ( Controller != None )
|
|
Controller.ChangedWeapon();
|
|
|
|
PendingWeapon = None;
|
|
|
|
if ( OldWeapon != None )
|
|
{
|
|
OldWeapon.SetDefaultDisplayProperties();
|
|
OldWeapon.DetachFromPawn(self);
|
|
OldWeapon.GotoState('Hidden');
|
|
OldWeapon.NetUpdateFrequency = 2;
|
|
}
|
|
|
|
if ( Weapon != None )
|
|
{
|
|
Weapon.NetUpdateFrequency = 100;
|
|
Weapon.AttachToPawn(self);
|
|
Weapon.BringUp(OldWeapon);
|
|
PlayWeaponSwitch(NewWeapon);
|
|
}
|
|
|
|
if ( Inventory != None )
|
|
Inventory.OwnerEvent('ChangedWeapon'); // tell inventory that weapon changed (in case any effect was being applied)
|
|
}
|
|
|
|
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;
|
|
|
|
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)
|
|
{
|
|
if (bIsCrouched)
|
|
PlayAnim(IdleCrouchAnim, 1.0, 0.0);
|
|
else
|
|
PlayAnim(IdleWeaponAnim, 1.0, 0.0);
|
|
|
|
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);
|
|
|
|
SetAnimFrame(0.5);
|
|
}
|
|
|
|
C = GetBoneCoords(HeadBone);
|
|
|
|
HeadLoc = C.Origin + (HeadHeight * HeadScale * AdditionalScale * C.XAxis);
|
|
|
|
// 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));
|
|
}
|
|
|
|
//==============
|
|
// Encroachment
|
|
event bool EncroachingOn( actor Other )
|
|
{
|
|
if ( Other.bWorldGeometry || Other.bBlocksTeleport )
|
|
return true;
|
|
|
|
if ( (Vehicle(Other) != None) && (Weapon != None) && Weapon.IsA('Translauncher') )
|
|
return true;
|
|
|
|
if ( ((Controller == None) || !Controller.bIsPlayer || bWarping) && (Pawn(Other) != None) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
event EncroachedBy( actor Other )
|
|
{
|
|
// Allow encroachment by Vehicles so they can push the pawn out of the way
|
|
if ( Pawn(Other) != None && Vehicle(Other) == None )
|
|
gibbedBy(Other);
|
|
}
|
|
|
|
function gibbedBy(actor Other)
|
|
{
|
|
if ( Role < ROLE_Authority )
|
|
return;
|
|
if ( Pawn(Other) != None )
|
|
Died(Pawn(Other).Controller, class'DamTypeTelefragged', Location);
|
|
else
|
|
Died(None, class'Gibbed', Location);
|
|
}
|
|
|
|
//Base change - if new base is pawn or decoration, damage based on relative mass and old velocity
|
|
// Also, non-players will jump off pawns immediately
|
|
function JumpOffPawn()
|
|
{
|
|
Velocity += (100 + CollisionRadius) * VRand();
|
|
Velocity.Z = 200 + CollisionHeight;
|
|
SetPhysics(PHYS_Falling);
|
|
bNoJumpAdjust = true;
|
|
if ( Controller != None )
|
|
Controller.SetFall();
|
|
}
|
|
|
|
singular event BaseChange()
|
|
{
|
|
local float decorMass;
|
|
|
|
if ( bInterpolating )
|
|
return;
|
|
if ( (base == None) && (Physics == PHYS_None) )
|
|
SetPhysics(PHYS_Falling);
|
|
// Pawns can only set base to non-pawns, or pawns which specifically allow it.
|
|
// Otherwise we do some damage and jump off.
|
|
else if ( Pawn(Base) != None && Base != DrivenVehicle )
|
|
{
|
|
if ( !Pawn(Base).bCanBeBaseForPawns )
|
|
{
|
|
Base.TakeDamage( (1-Velocity.Z/400)* Mass/Base.Mass, Self,Location,0.5 * Velocity , class'Crushed');
|
|
JumpOffPawn();
|
|
}
|
|
}
|
|
else if ( (Decoration(Base) != None) && (Velocity.Z < -400) )
|
|
{
|
|
decorMass = FMax(Decoration(Base).Mass, 1);
|
|
Base.TakeDamage((-2* Mass/decorMass * Velocity.Z/400), Self, Location, 0.5 * Velocity, class'Crushed');
|
|
}
|
|
}
|
|
|
|
event UpdateEyeHeight( float DeltaTime )
|
|
{
|
|
local float smooth, MaxEyeHeight;
|
|
local float OldEyeHeight;
|
|
local Actor HitActor;
|
|
local vector HitLocation,HitNormal;
|
|
|
|
if ( Controller == None )
|
|
{
|
|
EyeHeight = 0;
|
|
return;
|
|
}
|
|
if ( bTearOff )
|
|
{
|
|
EyeHeight = Default.BaseEyeheight;
|
|
bUpdateEyeHeight = false;
|
|
return;
|
|
}
|
|
HitActor = trace(HitLocation,HitNormal,Location + (CollisionHeight + MAXSTEPHEIGHT + 14) * vect(0,0,1),
|
|
Location + CollisionHeight * vect(0,0,1),true);
|
|
if ( HitActor == None )
|
|
MaxEyeHeight = CollisionHeight + MAXSTEPHEIGHT;
|
|
else
|
|
MaxEyeHeight = HitLocation.Z - Location.Z - 14;
|
|
|
|
if ( abs(Location.Z - OldZ) > 15 )
|
|
{
|
|
bJustLanded = false;
|
|
bLandRecovery = false;
|
|
}
|
|
|
|
// smooth up/down stairs
|
|
if ( !bJustLanded )
|
|
{
|
|
smooth = FMin(0.9, 10.0 * DeltaTime/Level.TimeDilation);
|
|
LandBob *= (1 - smooth);
|
|
if( Controller.WantsSmoothedView() )
|
|
{
|
|
OldEyeHeight = EyeHeight;
|
|
EyeHeight = FClamp((EyeHeight - Location.Z + OldZ) * (1 - smooth) + BaseEyeHeight * smooth,
|
|
-0.5 * CollisionHeight, MaxEyeheight);
|
|
}
|
|
else
|
|
EyeHeight = FMin(EyeHeight * ( 1 - smooth) + BaseEyeHeight * smooth, MaxEyeHeight);
|
|
}
|
|
else if ( bLandRecovery )
|
|
{
|
|
smooth = FMin(0.9, 10.0 * DeltaTime);
|
|
OldEyeHeight = EyeHeight;
|
|
EyeHeight = FMin(EyeHeight * ( 1 - 0.6*smooth) + BaseEyeHeight * 0.6*smooth, BaseEyeHeight);
|
|
LandBob *= (1 - smooth);
|
|
if ( Eyeheight >= BaseEyeheight - 1)
|
|
{
|
|
bJustLanded = false;
|
|
bLandRecovery = false;
|
|
Eyeheight = BaseEyeheight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
smooth = FMin(0.65, 10.0 * DeltaTime);
|
|
OldEyeHeight = EyeHeight;
|
|
EyeHeight = FMin(EyeHeight * (1 - 1.5*smooth), MaxEyeHeight);
|
|
LandBob += 0.03 * (OldEyeHeight - Eyeheight);
|
|
if ( (Eyeheight < 0.25 * BaseEyeheight + 1) || (LandBob > 3) )
|
|
{
|
|
bLandRecovery = true;
|
|
Eyeheight = 0.25 * BaseEyeheight + 1;
|
|
}
|
|
}
|
|
|
|
Controller.AdjustView(DeltaTime);
|
|
}
|
|
|
|
/* EyePosition()
|
|
Called by PlayerController to determine camera position in first person view. Returns
|
|
the offset from the Pawn's location at which to place the camera
|
|
*/
|
|
simulated function vector EyePosition()
|
|
{
|
|
return EyeHeight * vect(0,0,1) + WalkBob;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
simulated event Destroyed()
|
|
{
|
|
if ( Shadow != None )
|
|
Shadow.Destroy();
|
|
if ( Controller != None )
|
|
Controller.PawnDied(self);
|
|
if ( Level.NetMode == NM_Client )
|
|
return;
|
|
|
|
while ( Inventory != None )
|
|
Inventory.Destroy();
|
|
|
|
Weapon = None;
|
|
Super.Destroyed();
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Called immediately before gameplay begins.
|
|
//
|
|
event PreBeginPlay()
|
|
{
|
|
Super.PreBeginPlay();
|
|
Instigator = self;
|
|
DesiredRotation = Rotation;
|
|
if ( bDeleteMe )
|
|
return;
|
|
|
|
if ( BaseEyeHeight == 0 )
|
|
BaseEyeHeight = 0.8 * CollisionHeight;
|
|
EyeHeight = BaseEyeHeight;
|
|
|
|
if ( menuname == "" )
|
|
menuname = GetItemName(string(class));
|
|
}
|
|
|
|
event PostBeginPlay()
|
|
{
|
|
local AIScript A;
|
|
|
|
Super.PostBeginPlay();
|
|
SplashTime = 0;
|
|
SpawnTime = Level.TimeSeconds;
|
|
EyeHeight = BaseEyeHeight;
|
|
OldRotYaw = Rotation.Yaw;
|
|
|
|
// automatically add controller to pawns which were placed in level
|
|
// NOTE: pawns spawned during gameplay are not automatically possessed by a controller
|
|
if ( Level.bStartup && (Health > 0) && !bDontPossess )
|
|
{
|
|
// check if I have an AI Script
|
|
if ( AIScriptTag != '' )
|
|
{
|
|
ForEach AllActors(class'AIScript',A,AIScriptTag)
|
|
break;
|
|
// let the AIScript spawn and init my controller
|
|
if ( A != None )
|
|
{
|
|
A.SpawnControllerFor(self);
|
|
if ( Controller != None )
|
|
return;
|
|
}
|
|
}
|
|
if ( (ControllerClass != None) && (Controller == None) )
|
|
Controller = spawn(ControllerClass);
|
|
if ( Controller != None )
|
|
{
|
|
Controller.Possess(self);
|
|
AIController(Controller).Skill += SkillModifier;
|
|
}
|
|
}
|
|
}
|
|
|
|
// called after PostBeginPlay on net client
|
|
simulated event PostNetBeginPlay()
|
|
{
|
|
local playercontroller P;
|
|
|
|
if ( Level.bDropDetail || (Level.DetailMode == DM_Low) )
|
|
MaxLights = Min(4,MaxLights);
|
|
if ( Role == ROLE_Authority )
|
|
return;
|
|
if ( (Controller != None) && (Controller.Pawn == None) )
|
|
{
|
|
Controller.Pawn = self;
|
|
if ( (PlayerController(Controller) != None)
|
|
&& (PlayerController(Controller).ViewTarget == Controller) )
|
|
PlayerController(Controller).SetViewTarget(self);
|
|
}
|
|
|
|
if ( Role == ROLE_AutonomousProxy )
|
|
bUpdateEyeHeight = true;
|
|
|
|
if ( (PlayerReplicationInfo != None)
|
|
&& (PlayerReplicationInfo.Owner == None) )
|
|
{
|
|
PlayerReplicationInfo.SetOwner(Controller);
|
|
if ( left(PlayerReplicationInfo.PlayerName, 5) ~= "PRESS" )
|
|
{
|
|
P = Level.GetLocalPlayerController();
|
|
if ( (P.PlayerReplicationInfo != None) && !(left(PlayerReplicationInfo.PlayerName, 5) ~= "PRESS") )
|
|
bScriptPostRender = true;
|
|
}
|
|
}
|
|
PlayWaiting();
|
|
}
|
|
|
|
simulated function SetMesh()
|
|
{
|
|
if (Mesh != None)
|
|
return;
|
|
|
|
LinkMesh( default.mesh );
|
|
}
|
|
|
|
function Gasp();
|
|
function SetMovementPhysics();
|
|
|
|
function bool GiveHealth(int HealAmount, int HealMax)
|
|
{
|
|
if (Health < HealMax)
|
|
{
|
|
Health = Min(HealMax, Health + HealAmount);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool HasUDamage()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function int ShieldAbsorb( int damage )
|
|
{
|
|
return damage;
|
|
}
|
|
|
|
// if _RO_
|
|
function TakeDamage(int Damage, Pawn InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional int HitIndex)
|
|
// else UT
|
|
// function TakeDamage(int Damage, Pawn InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType);
|
|
{
|
|
local int actualDamage;
|
|
local Controller Killer;
|
|
|
|
if ( damagetype == None )
|
|
{
|
|
if ( InstigatedBy != None )
|
|
warn("No damagetype for damage by "$instigatedby$" with weapon "$InstigatedBy.Weapon);
|
|
DamageType = class'DamageType';
|
|
}
|
|
|
|
if ( Role < ROLE_Authority )
|
|
{
|
|
log(self$" client damage type "$damageType$" by "$instigatedBy);
|
|
return;
|
|
}
|
|
|
|
if ( Health <= 0 )
|
|
return;
|
|
|
|
if ((instigatedBy == None || instigatedBy.Controller == None) && DamageType.default.bDelayedDamage && DelayedDamageInstigatorController != None)
|
|
instigatedBy = DelayedDamageInstigatorController.Pawn;
|
|
|
|
if ( (Physics == PHYS_None) && (DrivenVehicle == None) )
|
|
SetMovementPhysics();
|
|
if (Physics == PHYS_Walking && damageType.default.bExtraMomentumZ)
|
|
momentum.Z = FMax(momentum.Z, 0.4 * VSize(momentum));
|
|
if ( instigatedBy == self )
|
|
momentum *= 0.6;
|
|
momentum = momentum/Mass;
|
|
|
|
if (Weapon != None)
|
|
Weapon.AdjustPlayerDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType );
|
|
if (DrivenVehicle != None)
|
|
DrivenVehicle.AdjustDriverDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType );
|
|
if ( (InstigatedBy != None) && InstigatedBy.HasUDamage() )
|
|
Damage *= 2;
|
|
actualDamage = Level.Game.ReduceDamage(Damage, self, instigatedBy, HitLocation, Momentum, DamageType);
|
|
if( DamageType.default.bArmorStops && (actualDamage > 0) )
|
|
actualDamage = ShieldAbsorb(actualDamage);
|
|
|
|
Health -= actualDamage;
|
|
if ( HitLocation == vect(0,0,0) )
|
|
HitLocation = Location;
|
|
|
|
PlayHit(actualDamage,InstigatedBy, hitLocation, damageType, Momentum);
|
|
if ( Health <= 0 )
|
|
{
|
|
// pawn died
|
|
if ( DamageType.default.bCausedByWorld && (instigatedBy == None || instigatedBy == self) && LastHitBy != None )
|
|
Killer = LastHitBy;
|
|
else if ( instigatedBy != None )
|
|
Killer = instigatedBy.GetKillerController();
|
|
if ( Killer == None && DamageType.Default.bDelayedDamage )
|
|
Killer = DelayedDamageInstigatorController;
|
|
// if _KF_
|
|
if ( bPhysicsAnimUpdate )
|
|
SetTearOffMomemtum(momentum);
|
|
//else
|
|
// if ( bPhysicsAnimUpdate )
|
|
// TearOffMomentum = momentum;
|
|
// endif _KF_
|
|
Died(Killer, damageType, HitLocation);
|
|
}
|
|
else
|
|
{
|
|
AddVelocity( momentum );
|
|
if ( Controller != None )
|
|
Controller.NotifyTakeHit(instigatedBy, HitLocation, actualDamage, DamageType, Momentum);
|
|
if ( instigatedBy != None && instigatedBy != self )
|
|
LastHitBy = instigatedBy.Controller;
|
|
}
|
|
MakeNoise(1.0);
|
|
}
|
|
|
|
function SetDelayedDamageInstigatorController(Controller C)
|
|
{
|
|
DelayedDamageInstigatorController = C;
|
|
}
|
|
|
|
simulated function int GetTeamNum()
|
|
{
|
|
if ( Controller != None )
|
|
return Controller.GetTeamNum();
|
|
if ( (DrivenVehicle != None) && (DrivenVehicle.Controller != None) )
|
|
return DrivenVehicle.Controller.GetTeamNum();
|
|
if ( (PlayerReplicationInfo == None) || (PlayerReplicationInfo.Team == None) )
|
|
return 255;
|
|
return PlayerReplicationInfo.Team.TeamIndex;
|
|
}
|
|
|
|
function TeamInfo GetTeam()
|
|
{
|
|
if ( PlayerReplicationInfo != None )
|
|
return PlayerReplicationInfo.Team;
|
|
if ( (DrivenVehicle != None) && (DrivenVehicle.PlayerReplicationInfo != None) )
|
|
return DrivenVehicle.PlayerReplicationInfo.Team;
|
|
return None;
|
|
}
|
|
|
|
function Controller GetKillerController()
|
|
{
|
|
return Controller;
|
|
}
|
|
|
|
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
|
{
|
|
local Vector TossVel;
|
|
local Trigger T;
|
|
local NavigationPoint N;
|
|
|
|
if ( bDeleteMe || Level.bLevelChange || Level.Game == None )
|
|
return; // already destroyed, or level is being cleaned up
|
|
|
|
if ( DamageType.default.bCausedByWorld && (Killer == None || Killer == Controller) && LastHitBy != None )
|
|
Killer = LastHitBy;
|
|
|
|
// mutator hook to prevent deaths
|
|
// WARNING - don't prevent bot suicides - they suicide when really needed
|
|
if ( Level.Game.PreventDeath(self, Killer, damageType, HitLocation) )
|
|
{
|
|
Health = max(Health, 1); //mutator should set this higher
|
|
return;
|
|
}
|
|
Health = Min(0, Health);
|
|
|
|
if ( Weapon != None && (DrivenVehicle == None || DrivenVehicle.bAllowWeaponToss) )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.LastPawnWeapon = Weapon.Class;
|
|
Weapon.HolderDied();
|
|
TossVel = Vector(GetViewRotation());
|
|
TossVel = TossVel * ((Velocity Dot TossVel) + 500) + Vect(0,0,200);
|
|
TossWeapon(TossVel);
|
|
}
|
|
|
|
if ( DrivenVehicle != None )
|
|
{
|
|
Velocity = DrivenVehicle.Velocity;
|
|
DrivenVehicle.DriverDied();
|
|
}
|
|
|
|
if ( Controller != None )
|
|
{
|
|
Controller.WasKilledBy(Killer);
|
|
Level.Game.Killed(Killer, Controller, self, damageType);
|
|
}
|
|
else
|
|
Level.Game.Killed(Killer, Controller(Owner), self, damageType);
|
|
|
|
DrivenVehicle = None;
|
|
|
|
if ( Killer != None )
|
|
TriggerEvent(Event, self, Killer.Pawn);
|
|
else
|
|
TriggerEvent(Event, self, None);
|
|
|
|
// make sure to untrigger any triggers requiring player touch
|
|
if ( IsPlayerPawn() || WasPlayerPawn() )
|
|
{
|
|
PhysicsVolume.PlayerPawnDiedInVolume(self);
|
|
ForEach TouchingActors(class'Trigger',T)
|
|
T.PlayerToucherDied(self);
|
|
|
|
// event for HoldObjectives
|
|
//for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
|
|
// if ( N.bStatic && N.bReceivePlayerToucherDiedNotify )
|
|
ForEach TouchingActors(class'NavigationPoint', N)
|
|
if ( N.bReceivePlayerToucherDiedNotify )
|
|
N.PlayerToucherDied( Self );
|
|
}
|
|
|
|
// remove powerup effects, etc.
|
|
RemovePowerups();
|
|
|
|
Velocity.Z *= 1.3;
|
|
if ( IsHumanControlled() )
|
|
PlayerController(Controller).ForceDeathUpdate();
|
|
if ( (DamageType != None) && DamageType.default.bAlwaysGibs )
|
|
ChunkUp( Rotation, DamageType.default.GibPerterbation );
|
|
else
|
|
{
|
|
NetUpdateFrequency = Default.NetUpdateFrequency;
|
|
PlayDying(DamageType, HitLocation);
|
|
if ( Level.Game.bGameEnded )
|
|
return;
|
|
if ( !bPhysicsAnimUpdate && !IsLocallyControlled() )
|
|
ClientDying(DamageType, HitLocation);
|
|
}
|
|
|
|
}
|
|
|
|
function RemovePowerups();
|
|
|
|
event Falling()
|
|
{
|
|
//SetPhysics(PHYS_Falling); //Note - physics changes type to PHYS_Falling by default
|
|
if ( Controller != None )
|
|
Controller.SetFall();
|
|
}
|
|
|
|
//#ifdef _KF_
|
|
// A section of the PickWallAdjust function has a hardcoded jump. This can override that.
|
|
event PickWallAdjustInLowGravity( vector WallHitNormal, actor HitActor );
|
|
//#endif
|
|
|
|
event HitWall(vector HitNormal, actor Wall);
|
|
|
|
event Landed(vector HitNormal)
|
|
{
|
|
ImpactVelocity = vect(0,0,0);
|
|
TakeFallingDamage();
|
|
if ( Health > 0 )
|
|
PlayLanded(Velocity.Z);
|
|
if ( (Velocity.Z < -200) && (PlayerController(Controller) != None) )
|
|
{
|
|
bJustLanded = PlayerController(Controller).bLandingShake;
|
|
OldZ = Location.Z;
|
|
}
|
|
LastHitBy = None;
|
|
}
|
|
|
|
event HeadVolumeChange(PhysicsVolume newHeadVolume)
|
|
{
|
|
if ( (Level.NetMode == NM_Client) || (Controller == None) )
|
|
return;
|
|
if ( HeadVolume.bWaterVolume )
|
|
{
|
|
if (!newHeadVolume.bWaterVolume)
|
|
{
|
|
if ( Controller.bIsPlayer && (BreathTime > 0) && (BreathTime < 8) )
|
|
Gasp();
|
|
BreathTime = -1.0;
|
|
}
|
|
}
|
|
else if ( newHeadVolume.bWaterVolume )
|
|
BreathTime = UnderWaterTime;
|
|
}
|
|
|
|
function bool TouchingWaterVolume()
|
|
{
|
|
local PhysicsVolume V;
|
|
|
|
ForEach TouchingActors(class'PhysicsVolume',V)
|
|
if ( V.bWaterVolume )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//Pain timer just expired.
|
|
//Check what zone I'm in (and which parts are)
|
|
//based on that cause damage, and reset BreathTime
|
|
|
|
function bool IsInPain()
|
|
{
|
|
local PhysicsVolume V;
|
|
|
|
ForEach TouchingActors(class'PhysicsVolume',V)
|
|
if ( V.bPainCausing && (V.DamageType != ReducedDamageType)
|
|
&& (V.DamagePerSec > 0) )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
event BreathTimer()
|
|
{
|
|
if ( (Health < 0) || (Level.NetMode == NM_Client) || (DrivenVehicle != None) )
|
|
return;
|
|
TakeDrowningDamage();
|
|
if ( Health > 0 )
|
|
BreathTime = 2.0;
|
|
}
|
|
|
|
function TakeDrowningDamage();
|
|
|
|
function bool CheckWaterJump(out vector WallNormal)
|
|
{
|
|
local actor HitActor;
|
|
local vector HitLocation, HitNormal, checkpoint, start, checkNorm;
|
|
|
|
if ( AIController(Controller) != None )
|
|
{
|
|
checkpoint = Acceleration;
|
|
checkpoint.Z = 0.0;
|
|
}
|
|
if ( checkpoint == vect(0,0,0))
|
|
checkpoint = vector(Rotation);
|
|
checkpoint.Z = 0.0;
|
|
checkNorm = Normal(checkpoint);
|
|
checkPoint = Location + 1.2 * CollisionRadius * checkNorm;
|
|
HitActor = Trace(HitLocation, HitNormal, checkpoint, Location, true, GetCollisionExtent());
|
|
if ( (HitActor != None) && (Pawn(HitActor) == None) )
|
|
{
|
|
WallNormal = -1 * HitNormal;
|
|
start = Location;
|
|
start.Z += 1.1 * MAXSTEPHEIGHT;
|
|
checkPoint = start + 2 * CollisionRadius * checkNorm;
|
|
HitActor = Trace(HitLocation, HitNormal, checkpoint, start, true);
|
|
if ( (HitActor == None) || (HitNormal.Z > 0.7) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function DoDoubleJump( bool bUpdating );
|
|
function bool CanDoubleJump();
|
|
function bool CanMultiJump();
|
|
|
|
function UpdateRocketAcceleration(float DeltaTime, float YawChange, float PitchChange);
|
|
// if _RO_
|
|
function HandleTurretRotation(float DeltaTime, float YawChange, float PitchChange);
|
|
// end _RO_
|
|
function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot);
|
|
|
|
function bool Dodge(eDoubleClickDir DoubleClickMove)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//#ifdef _KF_
|
|
// Alternate way to handle jumping that's level-designer controlled since they define the start and end positions with JumpSpot actors.
|
|
// Placed in Engine.Pawn because actors from 3 different packages can call this function.
|
|
function vector ComputeTrajectoryByTime( vector StartPosition, vector EndPosition, float fTimeEnd )
|
|
{
|
|
local vector NewVelocity;
|
|
local float ZAccel;
|
|
|
|
fTimeEnd = fTimeEnd * ( 1.1/Level.TimeDilation );
|
|
|
|
ZAccel = PhysicsVolume.Gravity.Z;
|
|
fTimeEnd = FMax( 0.0001f, fTimeEnd );
|
|
NewVelocity = ( EndPosition - StartPosition ) / fTimeEnd;
|
|
NewVelocity.z = ( ( EndPosition.z - StartPosition.z ) - ( 0.5f * ZAccel * ( fTimeEnd*fTimeEnd ) ) ) / fTimeEnd;
|
|
|
|
// return the inital velocity required to hit EndPosition at t = fTimeEnd
|
|
return NewVelocity;
|
|
}
|
|
//#endif
|
|
|
|
//Player Jumped
|
|
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) && (Level.Game.GameDifficulty > 2) )
|
|
MakeNoise(0.1 * Level.Game.GameDifficulty);
|
|
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;
|
|
else
|
|
Velocity.Z = JumpZ;
|
|
if ( (Base != None) && !Base.bWorldGeometry )
|
|
Velocity.Z += Base.Velocity.Z;
|
|
SetPhysics(PHYS_Falling);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* PlayMoverHitSound()
|
|
Mover Hit me, play appropriate sound if any
|
|
*/
|
|
function PlayMoverHitSound();
|
|
|
|
function PlayDyingSound();
|
|
|
|
function PlayHit(float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, optional int HitIndex)
|
|
{
|
|
local vector BloodOffset, Mo, HitNormal;
|
|
local class<Effects> DesiredEffect;
|
|
local class<Emitter> DesiredEmitter;
|
|
local PlayerController Hearer;
|
|
|
|
if ( DamageType == None )
|
|
return;
|
|
if ( (Damage <= 0) && ((Controller == None) || !Controller.bGodMode) )
|
|
return;
|
|
|
|
if (Damage > DamageType.Default.DamageThreshold) //spawn some blood
|
|
{
|
|
|
|
HitNormal = Normal(HitLocation - Location);
|
|
|
|
// Play any set effect
|
|
if ( EffectIsRelevant(Location,true) )
|
|
{
|
|
DesiredEffect = DamageType.static.GetPawnDamageEffect(HitLocation, Damage, Momentum, self, (Level.bDropDetail || Level.DetailMode == DM_Low));
|
|
|
|
if ( DesiredEffect != None )
|
|
{
|
|
BloodOffset = 0.2 * CollisionRadius * HitNormal;
|
|
BloodOffset.Z = BloodOffset.Z * 0.5;
|
|
|
|
Mo = Momentum;
|
|
if ( Mo.Z > 0 )
|
|
Mo.Z *= 0.5;
|
|
spawn(DesiredEffect,self,,HitLocation + BloodOffset, rotator(Mo));
|
|
}
|
|
|
|
// Spawn any preset emitter
|
|
|
|
DesiredEmitter = DamageType.Static.GetPawnDamageEmitter(HitLocation, Damage, Momentum, self, (Level.bDropDetail || Level.DetailMode == DM_Low));
|
|
if (DesiredEmitter != None)
|
|
spawn(DesiredEmitter,,,HitLocation+HitNormal, Rotator(HitNormal));
|
|
}
|
|
}
|
|
if ( Health <= 0 )
|
|
{
|
|
if ( PhysicsVolume.bDestructive && (PhysicsVolume.ExitActor != None) )
|
|
Spawn(PhysicsVolume.ExitActor);
|
|
return;
|
|
}
|
|
|
|
if ( Level.TimeSeconds - LastPainTime > 0.1 )
|
|
{
|
|
if ( InstigatedBy != None && (DamageType != None) && DamageType.default.bDirectDamage )
|
|
Hearer = PlayerController(InstigatedBy.Controller);
|
|
if ( Hearer != None )
|
|
Hearer.bAcuteHearing = true;
|
|
PlayTakeHit(HitLocation,Damage,damageType);
|
|
if ( Hearer != None )
|
|
Hearer.bAcuteHearing = false;
|
|
LastPainTime = Level.TimeSeconds;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Pawn was killed - detach any controller, and die
|
|
*/
|
|
|
|
// blow up into little pieces (implemented in subclass)
|
|
|
|
simulated function ChunkUp( Rotator HitRotation, float ChunkPerterbation )
|
|
{
|
|
if ( (Level.NetMode != NM_Client) && (Controller != None) )
|
|
{
|
|
if ( Controller.bIsPlayer )
|
|
Controller.PawnDied(self);
|
|
else
|
|
Controller.Destroy();
|
|
}
|
|
destroy();
|
|
}
|
|
|
|
simulated function TurnOff()
|
|
{
|
|
//ifdef _RO_
|
|
if ( PlayerReplicationInfo != none && PlayerReplicationInfo.SteamStatsAndAchievements != none )
|
|
{
|
|
PlayerReplicationInfo.SteamStatsAndAchievements.PlayerDied();
|
|
}
|
|
//endif
|
|
|
|
SetCollision(true,false);
|
|
AmbientSound = None;
|
|
bNoWeaponFiring = true;
|
|
Velocity = vect(0,0,0);
|
|
SetPhysics(PHYS_None);
|
|
bPhysicsAnimUpdate = false;
|
|
bIsIdle = true;
|
|
bWaitForAnim = false;
|
|
StopAnimating();
|
|
bIgnoreForces = true;
|
|
}
|
|
|
|
/* IsInLoadout()
|
|
return true if InventoryClass is part of required or optional equipment
|
|
*/
|
|
function bool IsInLoadout(class<Inventory> InventoryClass)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
State Dying
|
|
{
|
|
ignores Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer;
|
|
|
|
event ChangeAnimation() {}
|
|
event StopPlayFiring() {}
|
|
function PlayFiring(float Rate, name FiringMode) {}
|
|
function PlayWeaponSwitch(Weapon NewWeapon) {}
|
|
function PlayTakeHit(vector HitLoc, int Damage, class<DamageType> damageType) {}
|
|
simulated function PlayNextAnimation() {}
|
|
|
|
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
|
{
|
|
}
|
|
|
|
event FellOutOfWorld(eKillZType KillType)
|
|
{
|
|
if(KillType == KILLZ_Lava || KillType == KILLZ_Suicide )
|
|
return;
|
|
|
|
Destroy();
|
|
}
|
|
|
|
function Timer()
|
|
{
|
|
if ( !PlayerCanSeeMe() )
|
|
Destroy();
|
|
else
|
|
SetTimer(2.0, false);
|
|
}
|
|
|
|
function Landed(vector HitNormal)
|
|
{
|
|
local rotator finalRot;
|
|
|
|
if( Velocity.Z < -500 )
|
|
TakeDamage( (1-Velocity.Z/30),Instigator,Location,vect(0,0,0) , class'Crushed');
|
|
|
|
finalRot = Rotation;
|
|
finalRot.Roll = 0;
|
|
finalRot.Pitch = 0;
|
|
setRotation(finalRot);
|
|
SetPhysics(PHYS_None);
|
|
SetCollision(true, false);
|
|
|
|
if ( !IsAnimating(0) )
|
|
LieStill();
|
|
}
|
|
|
|
/* ReduceCylinder() made obsolete by ragdoll deaths */
|
|
function ReduceCylinder()
|
|
{
|
|
SetCollision(false,false);
|
|
}
|
|
|
|
function LandThump()
|
|
{
|
|
// animation notify - play sound if actually landed, and animation also shows it
|
|
if ( Physics == PHYS_None)
|
|
bThumped = true;
|
|
}
|
|
|
|
event AnimEnd(int Channel)
|
|
{
|
|
if ( Channel != 0 )
|
|
return;
|
|
if ( Physics == PHYS_None )
|
|
LieStill();
|
|
else if ( PhysicsVolume.bWaterVolume )
|
|
{
|
|
bThumped = true;
|
|
LieStill();
|
|
}
|
|
}
|
|
|
|
function LieStill()
|
|
{
|
|
if ( !bThumped )
|
|
LandThump();
|
|
ReduceCylinder();
|
|
}
|
|
|
|
singular function BaseChange()
|
|
{
|
|
if( base == None )
|
|
SetPhysics(PHYS_Falling);
|
|
else if ( Pawn(base) != None ) // don't let corpse ride around on someone's head
|
|
ChunkUp( Rotation, 1.0 );
|
|
}
|
|
|
|
function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
|
|
Vector momentum, class<DamageType> damageType, optional int HitIndex)
|
|
{
|
|
SetPhysics(PHYS_Falling);
|
|
if ( (Physics == PHYS_None) && (Momentum.Z < 0) )
|
|
Momentum.Z *= -1;
|
|
Velocity += 3 * momentum/(Mass + 200);
|
|
if ( bInvulnerableBody )
|
|
return;
|
|
Damage *= DamageType.Default.GibModifier;
|
|
Health -=Damage;
|
|
if ( ((Damage > 30) || !IsAnimating()) && (Health < -80) )
|
|
ChunkUp( Rotation, DamageType.default.GibPerterbation );
|
|
}
|
|
|
|
function BeginState()
|
|
{
|
|
local int i;
|
|
|
|
//log(self$" dying");
|
|
if ( bTearOff && (Level.NetMode == NM_DedicatedServer) )
|
|
LifeSpan = 1.0;
|
|
else
|
|
SetTimer(12.0, false);
|
|
SetPhysics(PHYS_Falling);
|
|
bInvulnerableBody = true;
|
|
if ( Controller != None )
|
|
{
|
|
if ( Controller.bIsPlayer )
|
|
Controller.PawnDied(self);
|
|
else
|
|
Controller.Destroy();
|
|
}
|
|
|
|
for (i = 0; i < Attached.length; i++)
|
|
if (Attached[i] != None)
|
|
Attached[i].PawnBaseDied();
|
|
}
|
|
|
|
Begin:
|
|
Sleep(0.2);
|
|
bInvulnerableBody = false;
|
|
PlayDyingSound();
|
|
}
|
|
|
|
//=============================================================================
|
|
// Animation interface for controllers
|
|
|
|
simulated event SetAnimAction(name NewAction);
|
|
|
|
/* PlayXXX() function called by controller to play transient animation actions
|
|
*/
|
|
simulated event PlayDying(class<DamageType> DamageType, vector HitLoc)
|
|
{
|
|
AmbientSound = None;
|
|
GotoState('Dying');
|
|
if ( bPhysicsAnimUpdate )
|
|
{
|
|
bReplicateMovement = false;
|
|
bTearOff = true;
|
|
Velocity += TearOffMomentum;
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
bPlayedDeath = true;
|
|
}
|
|
|
|
simulated function PlayFiring(optional float Rate, optional name FiringMode);
|
|
function PlayWeaponSwitch(Weapon NewWeapon);
|
|
simulated event StopPlayFiring()
|
|
{
|
|
bSteadyFiring = false;
|
|
}
|
|
|
|
function PlayTakeHit(vector HitLoc, int Damage, class<DamageType> damageType)
|
|
{
|
|
local Sound DesiredSound;
|
|
|
|
if (Damage==0)
|
|
return;
|
|
//
|
|
// Play a hit sound according to the DamageType
|
|
|
|
DesiredSound = DamageType.Static.GetPawnDamageSound();
|
|
if (DesiredSound != None)
|
|
PlayOwnedSound(DesiredSound,SLOT_Misc);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Pawn internal animation functions
|
|
|
|
simulated event ChangeAnimation()
|
|
{
|
|
if ( (Controller != None) && Controller.bControlAnimations )
|
|
return;
|
|
// player animation - set up new idle and moving animations
|
|
PlayWaiting();
|
|
PlayMoving();
|
|
}
|
|
|
|
simulated event AnimEnd(int Channel)
|
|
{
|
|
if ( Channel == 0 )
|
|
PlayWaiting();
|
|
}
|
|
|
|
// Animation group checks (usually implemented in subclass)
|
|
|
|
function bool CannotJumpNow()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated event PlayJump();
|
|
simulated event PlayFalling();
|
|
simulated function PlayMoving();
|
|
simulated function PlayWaiting();
|
|
|
|
function PlayLanded(float impactVel)
|
|
{
|
|
if ( !bPhysicsAnimUpdate )
|
|
PlayLandingAnimation(impactvel);
|
|
}
|
|
|
|
simulated event PlayLandingAnimation(float ImpactVel);
|
|
|
|
function PlayVictoryAnimation();
|
|
|
|
|
|
function Vehicle GetVehicleBase()
|
|
{
|
|
return Vehicle(Base);
|
|
}
|
|
|
|
function int GetSpree()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
function IncrementSpree();
|
|
|
|
// Allows a pawn to process the raw input from the controller
|
|
|
|
simulated function RawInput(float DeltaTime,
|
|
float aBaseX, float aBaseY, float aBaseZ, float aMouseX, float aMouseY,
|
|
float aForward, float aTurn, float aStrafe, float aUp, float aLookUp)
|
|
{
|
|
}
|
|
|
|
function Suicide()
|
|
{
|
|
KilledBy(self);
|
|
}
|
|
|
|
// Cheats - invoked by CheatManager
|
|
function bool CheatWalk()
|
|
{
|
|
UnderWaterTime = Default.UnderWaterTime;
|
|
SetCollision(true, true , true);
|
|
SetPhysics(PHYS_Walking);
|
|
bCollideWorld = true;
|
|
return true;
|
|
}
|
|
|
|
function bool CheatGhost()
|
|
{
|
|
UnderWaterTime = -1.0;
|
|
SetCollision(false, false, false);
|
|
bCollideWorld = false;
|
|
return true;
|
|
}
|
|
|
|
function bool CheatFly()
|
|
{
|
|
UnderWaterTime = Default.UnderWaterTime;
|
|
SetCollision(true, true , true);
|
|
bCollideWorld = true;
|
|
return true;
|
|
}
|
|
|
|
function float RangedAttackTime()
|
|
{
|
|
if ( Weapon != None )
|
|
return Weapon.RangedAttackTime();
|
|
|
|
return 0;
|
|
}
|
|
|
|
simulated function vector GetTargetLocation()
|
|
{
|
|
return Location;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
bCanBeDamaged=true
|
|
bNoRepMesh=true
|
|
bJumpCapable=true
|
|
bCanJump=true
|
|
bCanWalk=true
|
|
bCanSwim=false
|
|
bCanFly=false
|
|
bUpdateSimulatedPosition=true
|
|
BaseEyeHeight=+00064.000000
|
|
EyeHeight=+00054.000000
|
|
CollisionRadius=+00034.000000
|
|
CollisionHeight=+00078.000000
|
|
GroundSpeed=+00440.000000
|
|
AirSpeed=+00440.000000
|
|
WaterSpeed=+00300.000000
|
|
AccelRate=+02048.000000
|
|
JumpZ=+00420.000000
|
|
MaxFallSpeed=+1200.0
|
|
DrawType=DT_Mesh
|
|
bLOSHearing=true
|
|
HearingThreshold=+2800.0
|
|
Health=100
|
|
HealthMax=100
|
|
SuperHealthMax=199
|
|
Visibility=128
|
|
LadderSpeed=+200.0
|
|
noise1time=-00010.000000
|
|
noise2time=-00010.000000
|
|
AvgPhysicsTime=+00000.100000
|
|
SoundDampening=+00001.000000
|
|
DamageScaling=+00001.000000
|
|
bDirectional=True
|
|
bCanTeleport=True
|
|
bStasis=True
|
|
SoundRadius=160
|
|
SoundVolume=255
|
|
bCollideActors=True
|
|
bCollideWorld=True
|
|
bBlockActors=True
|
|
bProjTarget=True
|
|
bRotateToDesired=True
|
|
bCanCrouch=False
|
|
// if _RO_
|
|
bCanProne=False
|
|
// endif _RO_
|
|
RotationRate=(Pitch=4096,Yaw=20000,Roll=3072)
|
|
Texture=S_Pawn
|
|
RemoteRole=ROLE_SimulatedProxy
|
|
NetPriority=+00002.000000
|
|
AirControl=+0.05
|
|
ControllerClass=class'AIController'
|
|
CrouchHeight=+40.0
|
|
CrouchRadius=+34.0
|
|
MaxDesiredSpeed=+00001.000000
|
|
DesiredSpeed=+00001.000000
|
|
LandMovementState=PlayerWalking
|
|
WaterMovementState=PlayerSwimming
|
|
SightRadius=+05000.000000
|
|
bOwnerNoSee=true
|
|
bAcceptsProjectors=True
|
|
BlendChangeTime=0.25
|
|
WalkingPct=+0.5
|
|
CrouchedPct=+0.5
|
|
bTravel=true
|
|
ForwardStrafeBias=+0.0
|
|
BackwardStrafeBias=+0.0
|
|
bShouldBaseAtStartup=true
|
|
Bob=0.0060
|
|
bDisturbFluidSurface=true
|
|
bBlockKarma=False
|
|
bWeaponBob=true
|
|
bUseCompressedPosition=true
|
|
bCanDoubleJump=true
|
|
bSimulateGravity=true
|
|
HeadScale=+1.0
|
|
bServerMoveSetPawnRot=true
|
|
bNoCoronas=true
|
|
bSetPCRotOnPossess=true
|
|
DrivingHeight=+20
|
|
DrivingRadius=+22.0
|
|
HeadRadius=+9.0
|
|
HeadHeight=6.0
|
|
PitchUpLimit=18000
|
|
PitchDownLimit=49153
|
|
bCanUse=true
|
|
bAutoActivate=true
|
|
bUseDynamicLights=true
|
|
// if _KF_
|
|
LedgeCheckThreshold=4.0 // This is the engine's default value, but now it's configurable rather than hardcoded
|
|
AmbientSoundScaling=+6.83 // This is 100% volume at 255 now, instead of being clamped at like .11
|
|
//else
|
|
// AmbientSoundScaling=+0.8
|
|
// endif _KF_
|
|
|
|
|
|
// if _RO_
|
|
// RO Defaults
|
|
SprintPct=1.500000
|
|
CrouchedSprintPct=1.25
|
|
ProneIronPct=0.05
|
|
PronePct=0.260000
|
|
ProneHeight=15.0
|
|
ProneRadius=22.0
|
|
CustomAmbientRelevancyScale=1.0
|
|
}
|