3836 lines
115 KiB
Ucode
3836 lines
115 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.
|
|
//
|
|
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
|
//=============================================================================
|
|
class Pawn extends Actor
|
|
abstract
|
|
native(Pawn)
|
|
placeable
|
|
config(Game)
|
|
dependson(Controller)
|
|
nativereplication
|
|
implements(Interface_Speaker);
|
|
|
|
var const float MaxStepHeight,
|
|
MaxJumpHeight;
|
|
var const float WalkableFloorZ; /** minimum z value for floor normal (if less, not a walkable floor for this pawn) */
|
|
|
|
/** Used in determining if pawn is going off ledge. If the ledge is "shorter" than this value then the pawn will be able to walk off it. **/
|
|
var const float LedgeCheckThreshold;
|
|
var const Vector PartialLedgeMoveDir;
|
|
|
|
/** Controller currently possessing this Pawn */
|
|
var editinline repnotify Controller Controller;
|
|
|
|
/** Chained pawn list */
|
|
var const Pawn NextPawn;
|
|
|
|
/** Used for cacheing net relevancy test */
|
|
var float NetRelevancyTime;
|
|
var playerController LastRealViewer;
|
|
var actor LastViewer;
|
|
|
|
/** If true, call the script TickSpecial() event. */
|
|
var bool bScriptTickSpecial;
|
|
|
|
// Physics related flags.
|
|
var bool bUpAndOut; // used by swimming
|
|
var bool bIsWalking; // currently walking (can't jump, affects animations)
|
|
|
|
/** Physics to use when walking. Typically set to PHYS_Walking or PHYS_NavMeshWalking */
|
|
var(Movement) EPhysics WalkingPhysics;
|
|
|
|
// Crouching
|
|
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 const float UncrouchTime; // when auto-crouch during movement, continually try to uncrouch once this decrements to zero
|
|
var float CrouchHeight; // CollisionHeight when crouching
|
|
var float CrouchRadius; // CollisionRadius when crouching
|
|
var const int FullHeight; // cached for pathfinding
|
|
|
|
var bool bCrawler; // crawling - pitch and roll based on surface pawn is on
|
|
|
|
/** Used by movement natives to slow pawn as it reaches its destination to prevent overshooting */
|
|
var const bool bReducedSpeed;
|
|
|
|
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 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 bAllowLedgeOverhang;// if TRUE then allow the pawn to hang off ledges based on the cylinder extent
|
|
var const bool bPartiallyOverLedge;// if TRUE pawn was over a ledge without falling, allows us to handle case if player stops
|
|
var const bool bSimulateGravity; // simulate gravity for this pawn on network clients when predicting position (true if pawn is walking or falling)
|
|
var bool bIgnoreForces; // if true, not affected by external forces
|
|
var bool bCanWalkOffLedges; // Can still fall off ledges, even when walking (for Player Controlled pawns)
|
|
var bool bCanBeBaseForPawns; // all your 'base', are belong to us
|
|
var const bool bSimGravityDisabled; // used on network clients
|
|
var bool bDirectHitWall; // always call pawn hitwall directly (no controller notifyhitwall)
|
|
var const bool bPushesRigidBodies; // Will do a check to find nearby PHYS_RigidBody actors and will give them a 'soft' push.
|
|
var bool bForceFloorCheck; // force the pawn in PHYS_Walking to do a check for a valid floor even if he hasn't moved. Cleared after next floor check.
|
|
var bool bForceKeepAnchor; // Force ValidAnchor function to accept any non-NULL anchor as valid (used to override when we want to set anchor for path finding)
|
|
|
|
var config bool bCanMantle; // can this pawn mantle over cover
|
|
var config bool bCanClimbUp; // can this pawn climb up cover wall
|
|
var bool bCanClimbCeilings; // can this pawn climb ceiling nodes
|
|
var config bool bCanSwatTurn; // can this pawn swat turn between cover
|
|
var config bool bCanLeap; // can this pawn use LeapReachSpec
|
|
var config bool bCanCoverSlip; // can this pawn coverslip
|
|
|
|
/** if set, display "MAP HAS PATHING ERRORS" and message in the log when a Pawn fails a full path search */
|
|
var globalconfig bool bDisplayPathErrors;
|
|
|
|
// AI related flags
|
|
var bool bCanPickupInventory; // if true, will pickup inventory when touching pickup actors
|
|
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 bMuffledHearing; // can hear sounds through walls (but muffled - sound distance increased to double plus 4x the distance through walls
|
|
var(AI) bool bDontPossess; // if true, Pawn won't be possessed at game start
|
|
var bool bRollToDesired; // Update roll when turning to desired rotation (normally false)
|
|
var bool bStationary; // pawn can't move
|
|
|
|
var bool bCachedRelevant; // network relevancy caching flag
|
|
var bool bNoWeaponFiring; // TRUE indicates that weapon firing is disabled for this pawn
|
|
var bool bModifyReachSpecCost; // pawn should call virtual function to modify reach spec costs
|
|
var bool bModifyNavPointDest; // pawn should call virtual function to modify destination location when moving to nav point
|
|
/** set if Pawn counts as a vehicle for pathfinding checks (so don't use bBlockedForVehicles nodes, etc) */
|
|
var bool bPathfindsAsVehicle;
|
|
/** Pawn multiplies cost of NavigationPoints that don't have bPreferredVehiclePath set by this number */
|
|
var float NonPreferredVehiclePathMultiplier;
|
|
|
|
/** Previously used ShouldBypassSimulatedClientPhysics flag **/
|
|
var bool bPrevBypassSimulatedClientPhysics;
|
|
|
|
// AI basics.
|
|
enum EPathSearchType
|
|
{
|
|
PST_Default,
|
|
PST_Breadth,
|
|
PST_NewBestPathTo,
|
|
PST_Constraint,
|
|
};
|
|
var EPathSearchType PathSearchType;
|
|
|
|
/** List of search constraints for pathing */
|
|
var PathConstraint PathConstraintList;
|
|
var PathGoalEvaluator PathGoalList;
|
|
|
|
var float DesiredSpeed;
|
|
var float MaxDesiredSpeed;
|
|
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 const float AvgPhysicsTime; // Physics updating time monitoring (for AI monitoring reaching destinations)
|
|
var float Mass; // Mass of this pawn.
|
|
var float Buoyancy; // Water buoyancy. A ratio (1.0 = neutral buoyancy, 0.0 = no buoyancy)
|
|
var float MeleeRange; // Max range for melee attack (not including collision radii)
|
|
var const NavigationPoint Anchor; // current nearest path;
|
|
var const int AnchorItem; // Used to index into nav mesh polys
|
|
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 float SpawnTime; // worldinfo time when this pawn was spawned
|
|
var int MaxPitchLimit; // limit on view pitching
|
|
|
|
// Movement.
|
|
var bool bRunPhysicsWithNoController; // When there is no Controller, Walking Physics abort and force a velocity and acceleration of 0. Set this to TRUE to override.
|
|
var bool bForceMaxAccel; // ignores Acceleration component, and forces max AccelRate to drive Pawn at full velocity.
|
|
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 OutofWaterZ; /** z velocity applied when pawn tries to get out of water */
|
|
var float MaxOutOfWaterStepHeight; /** Maximum step height for getting out of water */
|
|
var bool bLimitFallAccel; // should acceleration be limited (by a factor of GroundSpeed and AirControl) when in PHYS_Falling?
|
|
var float AirControl; // amount of AirControl available to the pawn
|
|
var float WalkingPct; // pct. of running speed that walking speed is
|
|
var float MovementSpeedModifier; // a modifier that can be used to override the movement speed.
|
|
var float CrouchedPct; // pct. of running speed that crouched walking speed is
|
|
var float MaxFallSpeed; // max speed pawn can land without taking damage
|
|
|
|
/** AI will take paths that require a landing velocity less than (MaxFallSpeed * AIMaxFallSpeedFactor) */
|
|
var float AIMaxFallSpeedFactor;
|
|
|
|
var(Camera) float BaseEyeHeight; // Base eye height above collision center.
|
|
var(Camera) 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 transient PhysicsVolume HeadVolume; // physics volume of head
|
|
var() int Health; /** amount of health this Pawn has */
|
|
var() int HealthMax; /** normal maximum health of Pawn - defaults to default.Health unless explicitly set otherwise */
|
|
var bool bReplicateHealthToAll; /** if true, replicate this Pawn's health to all clients; otherwise, only if owned by or ViewTarget of a client */
|
|
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 float KismetDeathDelayTime;
|
|
|
|
/** RootMotion derived velocity calculated by APawn::CalcVelocity() (used when replaying client moves in net games (since can't rely on animation when replaying moves)) */
|
|
var vector RMVelocity;
|
|
|
|
/** this flag forces APawn::CalcVelocity() to just use RMVelocity directly */
|
|
var bool bForceRMVelocity;
|
|
|
|
/** this flag forces APawn::CalcVelocity() to never use root motion derived velocity */
|
|
var bool bForceRegularVelocity;
|
|
|
|
// 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 SoundDampening;
|
|
var float DamageScaling;
|
|
|
|
var localized string MenuName; // Name used for this pawn type in menus (e.g. player selection)
|
|
|
|
var class<AIController> ControllerClass; // default class to use when pawn is controlled by AI
|
|
|
|
var editinline RepNotify 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;
|
|
|
|
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 bool bPlayedDeath; // set when death animation has been played (used in network games)
|
|
|
|
var() SkeletalMeshComponent Mesh;
|
|
|
|
var CylinderComponent CylinderComponent;
|
|
|
|
var() float RBPushRadius; // Unreal units
|
|
var() float RBPushStrength;
|
|
|
|
var repnotify Vehicle DrivenVehicle;
|
|
|
|
var float AlwaysRelevantDistanceSquared; // always relevant to other clients if closer than this distance to viewer, and have controller
|
|
|
|
/** replicated to we can see where remote clients are looking */
|
|
var const byte RemoteViewPitch;
|
|
|
|
/** Radius that is checked for nearby vehicles when pressing use */
|
|
var() float VehicleCheckRadius;
|
|
|
|
var Controller LastHitBy; //give kill credit to this guy if hit momentum causes pawn to fall to his death
|
|
|
|
var() float ViewPitchMin;
|
|
var() float ViewPitchMax;
|
|
|
|
/** Max difference between pawn's Rotation.Yaw and DesiredRotation.Yaw for pawn to be considered as having reached its desired rotation */
|
|
var int AllowedYawError;
|
|
/** Desired Target Rotation : Physics will smoothly rotate actor to this rotation **/
|
|
/** In future I will uncomment this change. Currently Actor has the variable.**/
|
|
var(Movement) const rotator DesiredRotation;
|
|
/** DesiredRotation is set by somebody - Pawn's default behavior (using direction for desiredrotation) does not work **/
|
|
var const private{private} bool bDesiredRotationSet;
|
|
/** Do not overwrite current DesiredRotation **/
|
|
var const private{private} bool bLockDesiredRotation;
|
|
/** Unlock DesiredRotation when Reached to the destination
|
|
* This is used when bLockDesiredRotation=TRUE
|
|
* This will set bLockDesiredRotation = FALSE when reached to DesiredRotation
|
|
*/
|
|
var const private{private} bool bUnlockWhenReached;
|
|
/** Inventory Manager */
|
|
var class<InventoryManager> InventoryManagerClass;
|
|
var repnotify InventoryManager InvManager;
|
|
|
|
/** Weapon currently held by Pawn */
|
|
var Weapon Weapon;
|
|
|
|
/**
|
|
* This next group of replicated properties are used to cause 3rd person effects on
|
|
* remote clients. FlashLocation and FlashCount are altered by the weapon to denote that
|
|
* a shot has occured and FiringMode is used to determine what type of shot.
|
|
*/
|
|
|
|
/** Hit Location of instant hit weapons. vect(0,0,0) = not firing. */
|
|
var repnotify vector FlashLocation;
|
|
/** last FlashLocation that was an actual shot, i.e. not counting clears to (0,0,0)
|
|
* this is used to make sure we set unique values to FlashLocation for consecutive shots even when there was a clear in between,
|
|
* so that if a client missed the clear due to low net update rate, it still gets the new firing location
|
|
*/
|
|
var vector LastFiringFlashLocation;
|
|
/** increased when weapon fires. 0 = not firing. 1 - 255 = firing */
|
|
var repnotify byte FlashCount;
|
|
/** firing mode used when firing */
|
|
var repnotify byte FiringMode;
|
|
/** tracks the number of consecutive shots. Note that this is not replicated, so it's not correct on remote clients. It's only updated when the pawn is relevant. */
|
|
var int ShotCount;
|
|
|
|
/** set in InitRagdoll() to old CollisionComponent (since it must be Mesh for ragdolls) so that TermRagdoll() can restore it */
|
|
var PrimitiveComponent PreRagdollCollisionComponent;
|
|
|
|
/** Physics object created to create contacts with physics objects, used to push them around. */
|
|
var RB_BodyInstance PhysicsPushBody;
|
|
|
|
/** @HACK: count of times processLanded() was called but it failed without changing physics for some reason
|
|
* so we can detect and avoid a rare case where Pawns get stuck in that state
|
|
*/
|
|
var int FailedLandingCount;
|
|
|
|
/** Controls whether the pawn needs the base ticked before this one can be ticked */
|
|
var bool bNeedsBaseTickedFirst;
|
|
|
|
/** Array of Slots */
|
|
var transient Array<AnimNodeSlot> SlotNodes;
|
|
/** List of Matinee InterpGroup controlling this actor. */
|
|
var transient Array<InterpGroup> InterpGroupList;
|
|
|
|
/** AudioComponent used by FaceFX */
|
|
var transient protected AudioComponent FacialAudioComp;
|
|
|
|
/** General material used to control common pawn material parameters (e.g. burning) */
|
|
var protected transient MaterialInstanceConstant MIC_PawnMat;
|
|
var protected transient MaterialInstanceConstant MIC_PawnHair;
|
|
|
|
/** If TRUE, translation of mesh is updated to match desired floor translation (0 unless special move desired floor conforming) */
|
|
var transient repnotify bool bUsedByMatinee;
|
|
|
|
struct native ScalarParameterInterpStruct
|
|
{
|
|
/** Name of parameter to change */
|
|
var() Name ParameterName;
|
|
/** Desired Parameter Value */
|
|
var() float ParameterValue;
|
|
/** Desired Interpolation Time */
|
|
var() float InterpTime;
|
|
/** Time before interpolation starts */
|
|
var() float WarmUpTime;
|
|
};
|
|
var() Array<ScalarParameterInterpStruct> ScalarParameterInterpArray;
|
|
|
|
/** Whether root motion should be extracted from the interp curve or not */
|
|
/** NOTE: Currently assumes blending isn't altering the root bone */
|
|
var bool bRootMotionFromInterpCurve;
|
|
var RootMotionCurve RootMotionInterpCurve;
|
|
var float RootMotionInterpRate;
|
|
var float RootMotionInterpCurrentTime;
|
|
var Vector RootMotionInterpCurveLastValue;
|
|
|
|
//debug
|
|
var(Debug) bool bDebugShowCameraLocation;
|
|
|
|
/**
|
|
* Set this to TRUE if riding on a moving base that you know is clear from non-moving world obstructions.
|
|
* This can solve move-order dependencies when riding a mover, and it's faster.
|
|
*/
|
|
var() bool bFastAttachedMove;
|
|
|
|
`if(`__TW_)
|
|
var bool bCanJumpOverWalls;
|
|
var bool bDebugCrawlerPhysics;
|
|
`endif
|
|
|
|
`if(`__TW_LIGHTING_MODIFICATIONS_) // Custom lighting channel implementation
|
|
/** Keeps track of how many lighting volumes the pawn is currently in */
|
|
var transient byte LightingVolumeEnterCount;
|
|
`endif
|
|
|
|
cpptext
|
|
{
|
|
// declare type for node evaluation functions
|
|
typedef FLOAT ( *NodeEvaluator ) (ANavigationPoint*, APawn*, FLOAT);
|
|
|
|
virtual void PostBeginPlay();
|
|
virtual void PostScriptDestroyed();
|
|
|
|
// AActor interface.
|
|
|
|
APawn* GetPlayerPawn() const;
|
|
virtual FLOAT GetNetPriority(const FVector& ViewPos, const FVector& ViewDir, APlayerController* Viewer, UActorChannel* InChannel, FLOAT Time, UBOOL bLowBandwidth);
|
|
virtual INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel );
|
|
virtual void NotifyBump(AActor *Other, UPrimitiveComponent* OtherComp, const FVector &HitNormal);
|
|
virtual void TickSimulated( FLOAT DeltaSeconds );
|
|
virtual void TickSpecial( FLOAT DeltaSeconds );
|
|
UBOOL PlayerControlled();
|
|
virtual void SetBase(AActor *NewBase, FVector NewFloor = FVector(0,0,1), INT bNotifyActor=1, USkeletalMeshComponent* SkelComp=NULL, FName BoneName=NAME_None );
|
|
#if WITH_EDITOR
|
|
virtual void EditorApplyRotation(const FRotator& DeltaRotation, UBOOL bAltDown, UBOOL bShiftDown, UBOOL bCtrlDown);
|
|
virtual void CheckForErrors();
|
|
#endif
|
|
virtual UBOOL IsNetRelevantFor(APlayerController* RealViewer, AActor* Viewer, const FVector& SrcLocation);
|
|
UBOOL CacheNetRelevancy(UBOOL bIsRelevant, APlayerController* RealViewer, AActor* Viewer);
|
|
virtual UBOOL ShouldTrace(UPrimitiveComponent* Primitive,AActor *SourceActor, DWORD TraceFlags);
|
|
virtual void PreNetReceive();
|
|
virtual void PostNetReceiveLocation();
|
|
virtual void SmoothCorrection(const FVector& OldLocation);
|
|
virtual APawn* GetAPawn() { return this; }
|
|
virtual const APawn* GetAPawn() const { return this; }
|
|
|
|
/**
|
|
* Used for SkelControlLimb in BCS_BaseMeshSpace mode.
|
|
*
|
|
* @param SkelControlled - the mesh being modified by the skel control
|
|
*
|
|
* @return the skeletal mesh component to use for the transform calculation
|
|
*/
|
|
virtual USkeletalMeshComponent* GetMeshForSkelControlLimbTransform(const USkeletalMeshComponent* SkelControlled) { return Mesh; }
|
|
|
|
/**
|
|
* Sets the hard attach flag by first handling the case of already being
|
|
* based upon another actor
|
|
*
|
|
* @param bNewHardAttach the new hard attach setting
|
|
*/
|
|
virtual void SetHardAttach(UBOOL bNewHardAttach);
|
|
|
|
virtual UBOOL CanCauseFractureOnTouch()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Level functions
|
|
void SetZone( UBOOL bTest, UBOOL bForceRefresh );
|
|
|
|
// AI sensing
|
|
virtual void CheckNoiseHearing(AActor* NoiseMaker, FLOAT Loudness, FName NoiseType=NAME_None );
|
|
virtual FLOAT DampenNoise(AActor* NoiseMaker, FLOAT Loudness, FName NoiseType=NAME_None );
|
|
|
|
|
|
// Latent movement
|
|
virtual void setMoveTimer(FVector MoveDir);
|
|
FLOAT GetMaxSpeed();
|
|
virtual UBOOL moveToward(const FVector &Dest, AActor *GoalActor);
|
|
virtual UBOOL IsGlider();
|
|
virtual void rotateToward(FVector FocalPoint);
|
|
void StartNewSerpentine(const FVector& Dir, const FVector& Start);
|
|
void ClearSerpentine();
|
|
virtual UBOOL SharingVehicleWith(APawn *P);
|
|
#if __TW_PATHFINDING_
|
|
virtual void InitSerpentine();
|
|
virtual UBOOL TestWalkFall(FVector GravDir, FVector &CurrentPosition, FVector CollisionExtent, FCheckResult& Hit, AActor* GoalActor, const FVector StartLocation);
|
|
#else
|
|
void InitSerpentine();
|
|
#endif
|
|
virtual void HandleSerpentineMovement(FVector& out_Direction, FLOAT Distance, const FVector& Dest);
|
|
|
|
// reach tests
|
|
virtual UBOOL ReachedDestination(const FVector &Start, const FVector &Dest, AActor* GoalActor, UBOOL bCheckHandle=FALSE);
|
|
virtual int pointReachable(FVector aPoint, int bKnowVisible=0);
|
|
virtual int actorReachable(AActor *Other, UBOOL bKnowVisible=0, UBOOL bNoAnchorCheck=0);
|
|
virtual int Reachable(FVector aPoint, AActor* GoalActor);
|
|
int walkReachable(const FVector &Dest, const FVector &Start, int reachFlags, AActor* GoalActor);
|
|
int flyReachable(const FVector &Dest, const FVector &Start, int reachFlags, AActor* GoalActor);
|
|
int swimReachable(const FVector &Dest, const FVector &Start, int reachFlags, AActor* GoalActor);
|
|
int ladderReachable(const FVector &Dest, const FVector &Start, int reachFlags, AActor* GoalActor);
|
|
INT spiderReachable( const FVector &Dest, const FVector &Start, INT reachFlags, AActor* GoalActor );
|
|
FVector GetGravityDirection();
|
|
virtual UBOOL TryJumpUp(FVector Dir, FVector Destination, DWORD TraceFlags, UBOOL bNoVisibility);
|
|
virtual UBOOL ReachedBy(APawn* P, const FVector& TestPosition, const FVector& Dest);
|
|
virtual UBOOL ReachThresholdTest(const FVector &TestPosition, const FVector &Dest, AActor* GoalActor, FLOAT UpThresholdAdjust, FLOAT DownThresholdAdjust, FLOAT ThresholdAdjust);
|
|
virtual UBOOL SetHighJumpFlag() { return false; }
|
|
|
|
// movement component tests (used by reach tests)
|
|
void TestMove(const FVector &Delta, FVector &CurrentPosition, FCheckResult& Hit, const FVector &CollisionExtent);
|
|
FVector GetDefaultCollisionSize();
|
|
FVector GetCrouchSize();
|
|
ETestMoveResult walkMove(FVector Delta, FVector &CurrentPosition, const FVector &CollisionExtent, FCheckResult& Hit, AActor* GoalActor, FLOAT threshold);
|
|
ETestMoveResult flyMove(FVector Delta, FVector &CurrentPosition, AActor* GoalActor, FLOAT threshold);
|
|
ETestMoveResult swimMove(FVector Delta, FVector &CurrentPosition, AActor* GoalActor, FLOAT threshold);
|
|
virtual ETestMoveResult FindBestJump(FVector Dest, FVector &CurrentPosition);
|
|
virtual ETestMoveResult FindJumpUp(FVector Direction, FVector &CurrentPosition);
|
|
ETestMoveResult HitGoal(AActor *GoalActor);
|
|
virtual UBOOL HurtByDamageType(class UClass* DamageType);
|
|
UBOOL CanCrouchWalk( const FVector& StartLocation, const FVector& EndLocation, AActor* HitActor );
|
|
/** updates the highest landing Z axis velocity encountered during a reach test */
|
|
virtual void SetMaxLandingVelocity(FLOAT NewLandingVelocity) {}
|
|
|
|
// Path finding
|
|
UBOOL GeneratePath();
|
|
FLOAT findPathToward(AActor *goal, FVector GoalLocation, NodeEvaluator NodeEval, FLOAT BestWeight, UBOOL bWeightDetours, INT MaxPathLength = 0, UBOOL bReturnPartial = FALSE, INT SoftMaxNodes = 200);
|
|
ANavigationPoint* BestPathTo(NodeEvaluator NodeEval, ANavigationPoint *start, FLOAT *Weight, UBOOL bWeightDetours, INT MaxPathLength = 0, INT SoftMaxNodes = 200);
|
|
virtual ANavigationPoint* CheckDetour(ANavigationPoint* BestDest, ANavigationPoint* Start, UBOOL bWeightDetours);
|
|
virtual INT calcMoveFlags();
|
|
/** returns the maximum falling speed an AI will accept along a path */
|
|
FORCEINLINE FLOAT GetAIMaxFallSpeed() { return MaxFallSpeed * AIMaxFallSpeedFactor; }
|
|
virtual void MarkEndPoints(ANavigationPoint* EndAnchor, AActor* Goal, const FVector& GoalLocation);
|
|
virtual FLOAT SecondRouteAttempt(ANavigationPoint* Anchor, ANavigationPoint* EndAnchor, NodeEvaluator NodeEval, FLOAT BestWeight, AActor *goal, const FVector& GoalLocation, FLOAT StartDist, FLOAT EndDist, INT MaxPathLength, INT SoftMaxNodes);
|
|
/** finds the closest NavigationPoint within MAXPATHDIST that is usable by this pawn and directly reachable to/from TestLocation
|
|
* @param TestActor the Actor to find an anchor for
|
|
* @param TestLocation the location to find an anchor for
|
|
* @param bStartPoint true if we're finding the start point for a path search, false if we're finding the end point
|
|
* @param bOnlyCheckVisible if true, only check visibility - skip reachability test
|
|
* @param Dist (out) if an anchor is found, set to the distance TestLocation is from it. Set to 0.f if the anchor overlaps TestLocation
|
|
* @return a suitable anchor on the navigation network for reaching TestLocation, or NULL if no such point exists
|
|
*/
|
|
ANavigationPoint* FindAnchor(AActor* TestActor, const FVector& TestLocation, UBOOL bStartPoint, UBOOL bOnlyCheckVisible, FLOAT& Dist);
|
|
virtual INT ModifyCostForReachSpec( UReachSpec* Spec, INT Cost ) { return 0; }
|
|
virtual void InitForPathfinding( AActor* Goal, ANavigationPoint* EndAnchor ) {}
|
|
// allows pawn subclasses to veto anchor validity
|
|
virtual UBOOL IsValidAnchor( ANavigationPoint* AnchorCandidate ){ return TRUE; }
|
|
|
|
/*
|
|
* Route finding notifications (sent to target)
|
|
*/
|
|
virtual ANavigationPoint* SpecifyEndAnchor(APawn* RouteFinder);
|
|
virtual UBOOL AnchorNeedNotBeReachable();
|
|
virtual void NotifyAnchorFindingResult(ANavigationPoint* EndAnchor, APawn* RouteFinder);
|
|
|
|
// Pawn physics modes
|
|
virtual void setPhysics(BYTE NewPhysics, AActor* NewFloor = NULL, FVector NewFloorV = FVector(0,0,1));
|
|
virtual void performPhysics(FLOAT DeltaSeconds);
|
|
/** Called in PerformPhysics(), after StartNewPhysics() is done moving the Actor, and before the PendingTouch() event is dispatched. */
|
|
virtual void PostProcessPhysics( FLOAT DeltaSeconds, const FVector& OldVelocity );
|
|
/** If TRUE, bypass simulated client physics, and run owned/server physics instead. Do not perform simulation/correction. */
|
|
virtual UBOOL ShouldBypassSimulatedClientPhysics();
|
|
virtual FVector CheckForLedges(FVector AccelDir, FVector Delta, FVector GravDir, int &bCheckedFall, int &bMustJump );
|
|
virtual void physWalking(FLOAT deltaTime, INT Iterations);
|
|
virtual void physNavMeshWalking(FLOAT deltaTime);
|
|
virtual void physFlying(FLOAT deltaTime, INT Iterations);
|
|
virtual void physSwimming(FLOAT deltaTime, INT Iterations);
|
|
virtual void physFalling(FLOAT deltaTime, INT Iterations);
|
|
virtual void physSpider(FLOAT deltaTime, INT Iterations);
|
|
virtual void physLadder(FLOAT deltaTime, INT Iterations);
|
|
virtual void startNewPhysics(FLOAT deltaTime, INT Iterations);
|
|
virtual void GetNetBuoyancy(FLOAT &NetBuoyancy, FLOAT &NetFluidFriction);
|
|
virtual void StartFalling(INT Iterations, FLOAT remainingTime, FLOAT timeTick, const FVector& Delta, const FVector& subLoc);
|
|
virtual UBOOL ShouldCatchAir(const FVector& OldFloor, const FVector& Floor);
|
|
void startSwimming(FVector OldLocation, FVector OldVelocity, FLOAT timeTick, FLOAT remainingTime, INT Iterations);
|
|
virtual void physicsRotation(FLOAT deltaTime, FVector OldVelocity);
|
|
void processLanded(FVector const& HitNormal, AActor *HitActor, FLOAT remainingTime, INT Iterations);
|
|
virtual void SetPostLandedPhysics(AActor *HitActor, FVector HitNormal);
|
|
virtual void processHitWall(FCheckResult const& Hit, FLOAT TimeSlice=0.f);
|
|
virtual void Crouch(INT bClientSimulation=0);
|
|
virtual void UnCrouch(INT bClientSimulation=0);
|
|
void SmoothHitWall(FVector const& HitNormal, AActor *HitActor);
|
|
virtual FVector NewFallVelocity(FVector OldVelocity, FVector OldAcceleration, FLOAT timeTick);
|
|
void stepUp(const FVector& GravDir, const FVector& DesiredDir, const FVector& Delta, FCheckResult &Hit);
|
|
virtual FLOAT MaxSpeedModifier();
|
|
virtual FLOAT GetMaxAccel( FLOAT SpeedModifier = 1.f );
|
|
virtual FVector CalculateSlopeSlide(const FVector& Adjusted, const FCheckResult& Hit);
|
|
virtual UBOOL IgnoreBlockingBy(const AActor* Other) const;
|
|
virtual void PushedBy(AActor* Other);
|
|
virtual void UpdateBasedRotation(FRotator &FinalRotation, const FRotator& ReducedRotation);
|
|
virtual void ReverseBasedRotation();
|
|
|
|
virtual void InitRBPhys();
|
|
virtual void TermRBPhys(FRBPhysScene* Scene);
|
|
|
|
/** Update information used to detect overlaps between this actor and physics objects, used for 'pushing' things */
|
|
virtual void UpdatePushBody();
|
|
|
|
/** Called when the push body 'sensor' overlaps a physics body. Allows you to add a force to that body to move it. */
|
|
virtual void ProcessPushNotify(const FRigidBodyCollisionInfo& PushedInfo, const TArray<FRigidBodyContactInfo>& ContactInfos);
|
|
|
|
virtual UBOOL HasAudibleAmbientSound(const FVector& SrcLocation) { return FALSE; }
|
|
|
|
//superville: Chance for pawn to say he has reached a location w/o touching it (ie cover slot)
|
|
virtual UBOOL HasReached( ANavigationPoint *Nav, UBOOL& bFinalDecision ) { return FALSE; }
|
|
|
|
virtual FVector GetIdealCameraOrigin()
|
|
{
|
|
return FVector(Location.X,Location.Y,Location.Z + BaseEyeHeight);
|
|
}
|
|
|
|
/**
|
|
* Checks whether this pawn needs to have its base ticked first and does so if requested
|
|
*
|
|
* @return TRUE if the actor was ticked, FALSE if it was aborted (e.g. because it's in stasis)
|
|
*/
|
|
virtual UBOOL Tick( FLOAT DeltaTime, enum ELevelTick TickType );
|
|
|
|
/** Build AnimSet list, called by UpdateAnimSetList() */
|
|
virtual void BuildAnimSetList();
|
|
void RestoreAnimSetsToDefault();
|
|
|
|
// AnimControl Matinee Track support
|
|
|
|
/** Used to provide information on the slots that this Actor provides for animation to Matinee. */
|
|
virtual void GetAnimControlSlotDesc(TArray<struct FAnimSlotDesc>& OutSlotDescs);
|
|
|
|
/**
|
|
* Called by Matinee when we open it to start controlling animation on this Actor.
|
|
* Is also called again when the GroupAnimSets array changes in Matinee, so must support multiple calls.
|
|
*/
|
|
virtual void PreviewBeginAnimControl(class UInterpGroup* InInterpGroup);
|
|
|
|
/** Called each frame by Matinee to update the desired sequence by name and position within it. */
|
|
virtual void PreviewSetAnimPosition(FName SlotName, INT ChannelIndex, FName InAnimSeqName, FLOAT InPosition, UBOOL bLooping, UBOOL bFireNotifies, UBOOL bEnableRootMotion, FLOAT DeltaTime);
|
|
|
|
/** Called each frame by Matinee to update the desired animation channel weights for this Actor. */
|
|
virtual void PreviewSetAnimWeights(TArray<FAnimSlotInfo>& SlotInfos);
|
|
|
|
/** Called by Matinee when we close it after we have been controlling animation on this Actor. */
|
|
virtual void PreviewFinishAnimControl(class UInterpGroup* InInterpGroup);
|
|
|
|
/** Function used to control FaceFX animation in the editor (Matinee). */
|
|
virtual void PreviewUpdateFaceFX(UBOOL bForceAnim, const FString& GroupName, const FString& SeqName, FLOAT InPosition);
|
|
|
|
|
|
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
|
|
/** Used by Matinee playback to start a FaceFX animation playing. */
|
|
virtual void PreviewActorPlayFaceFX(const FString& GroupName, const FString& SeqName, UAkBaseSoundObject* InSoundCue);
|
|
// WWISEMODIF_END
|
|
|
|
/** Used by Matinee to stop current FaceFX animation playing. */
|
|
virtual void PreviewActorStopFaceFX();
|
|
|
|
/** Used in Matinee to get the AudioComponent we should play facial animation audio on. */
|
|
virtual UAudioComponent* PreviewGetFaceFXAudioComponent();
|
|
|
|
/** Get the UFaceFXAsset that is currently being used by this Actor when playing facial animations. */
|
|
virtual class UFaceFXAsset* PreviewGetActorFaceFXAsset();
|
|
|
|
/** Called each frame by Matinee to update the weight of a particular MorphNodeWeight. */
|
|
virtual void PreviewSetMorphWeight(FName MorphNodeName, FLOAT MorphWeight);
|
|
|
|
/** Called each frame by Matinee to update the scaling on a SkelControl. */
|
|
virtual void PreviewSetSkelControlScale(FName SkelControlName, FLOAT Scale);
|
|
|
|
/** Called each frame by Matinee to update the controlstrength on a SkelControl. */
|
|
virtual void SetSkelControlStrength(FName SkelControlName, FLOAT ControlStrength);
|
|
|
|
/** Called each from while the Matinee action is running, to set the animation weights for the actor. */
|
|
virtual void SetAnimWeights( const TArray<struct FAnimSlotInfo>& SlotInfos );
|
|
|
|
/** Called each frame by Matinee for InterpMoveTrack to adjust their location/rotation **/
|
|
virtual void AdjustInterpTrackMove(FVector& Pos, FRotator& Rot, FLOAT DeltaTime, UBOOL bIgnoreRotation = FALSE);
|
|
|
|
virtual UBOOL FindInterpMoveTrack(class UInterpTrackMove** MoveTrack, class UInterpTrackInstMove** MoveTrackInst, class USeqAct_Interp** OutSeq);
|
|
|
|
void UpdateScalarParameterInterp(FLOAT DeltaTime);
|
|
|
|
#if __TW_PHYSICS_
|
|
virtual void physicsRotationCrawler(FLOAT deltaTime, FRotator deltaRot, FRotator& NewRotation) {}
|
|
#endif
|
|
|
|
protected:
|
|
virtual void ApplyVelocityBraking(FLOAT DeltaTime, FLOAT Friction);
|
|
virtual void CalcVelocity(FVector &AccelDir, FLOAT DeltaTime, FLOAT MaxSpeed, FLOAT Friction, INT bFluid, INT bBrake, INT bBuoyant);
|
|
#if __TW_
|
|
int findNewFloor(FVector OldLocation, FLOAT deltaTime, FLOAT remainingTime, INT Iterations);
|
|
#endif
|
|
|
|
private:
|
|
UBOOL Pick3DWallAdjust(FVector WallHitNormal, AActor* HitActor);
|
|
FLOAT Swim(FVector Delta, FCheckResult &Hit);
|
|
FVector findWaterLine(FVector Start, FVector End);
|
|
void SpiderstepUp(const FVector& DesiredDir, const FVector& Delta, FCheckResult &Hit);
|
|
#if !__TW_
|
|
int findNewFloor(FVector OldLocation, FLOAT deltaTime, FLOAT remainingTime, INT Iterations);
|
|
#endif
|
|
int checkFloor(FVector Dir, FCheckResult &Hit);
|
|
}
|
|
|
|
replication
|
|
{
|
|
// Variables the server should send ALL clients.
|
|
if( bNetDirty )
|
|
FlashLocation, bSimulateGravity, bIsWalking, PlayerReplicationInfo, HitDamageType,
|
|
TakeHitLocation, DrivenVehicle, bUsedByMatinee, bFastAttachedMove;
|
|
if (bNetDirty && (bNetOwner || bReplicateHealthToAll /* || IsViewTargetOfReplicationViewer() */))
|
|
Health;
|
|
|
|
// variables sent to owning client
|
|
if ( bNetDirty && bNetOwner )
|
|
InvManager, Controller, GroundSpeed, WaterSpeed, AirSpeed, AccelRate, JumpZ, AirControl;
|
|
if (bNetDirty && bNetOwner && bNetInitial)
|
|
bCanSwatTurn;
|
|
|
|
// sent to non owning clients
|
|
if ( bNetDirty && (!bNetOwner || bDemoRecording) )
|
|
bIsCrouched, FlashCount, FiringMode;
|
|
|
|
// variable sent to all clients when Pawn has been torn off. (bTearOff)
|
|
if( bTearOff && bNetDirty )
|
|
TearOffMomentum;
|
|
|
|
// variables sent to all but the owning client
|
|
if ( (!bNetOwner || bDemoRecording) )
|
|
RemoteViewPitch;
|
|
|
|
if( bNetInitial && !bNetOwner )
|
|
bRootMotionFromInterpCurve;
|
|
|
|
if( bNetInitial && !bNetOwner && bRootMotionFromInterpCurve )
|
|
RootMotionInterpRate, RootMotionInterpCurrentTime, RootMotionInterpCurveLastValue;
|
|
|
|
// Replicated to ALL
|
|
if( ( Role == Role_Authority ) && bNetDirty )
|
|
HealthMax;
|
|
}
|
|
|
|
native final function bool PickWallAdjust(Vector WallHitNormal, Actor HitActor);
|
|
|
|
/** DesiredRotation related function **/
|
|
/** SetDesiredRotation function
|
|
* @param TargetDesiredRotation: DesiredRotation you want
|
|
* @param InLockDesiredRotation: I'd like to lock up DesiredRotation, please nobody else can touch it until I say it's done
|
|
* @param InUnlockWhenReached: When you lock, set this to TRUE if you want it to be auto Unlock when reached desired rotation
|
|
* @param InterpolationTime: Give interpolation time to get to the desired rotation - Ignore default RotationRate, but use this to get there
|
|
* @return TRUE if properly set, otherwise, return FALSE
|
|
**/
|
|
native final function bool SetDesiredRotation(Rotator TargetDesiredRotation, bool InLockDesiredRotation=FALSE, bool InUnlockWhenReached=FALSE, FLOAT InterpolationTime=-1.f, bool bResetRotationRate=TRUE);
|
|
|
|
/** LockDesiredRotation function
|
|
* @param Lock: Lock or Unlock CurrentDesiredRotation
|
|
* @param InUnlockWhenReached: Unlock when reached desired rotation. This is only valid when Lock = true
|
|
*/
|
|
native final function LockDesiredRotation(bool Lock, bool InUnlockWhenReached=false/** This is only valid if Lock=true **/);
|
|
/** ResetDesiredRotation function
|
|
* Clear RotationRate/Flag to go back to default behavior
|
|
* Unless it's locked.
|
|
*/
|
|
native final function ResetDesiredRotation();
|
|
/** CheckDesiredRotation function
|
|
* Check to see if DesiredRotation is met, and it need to be clear or not
|
|
* This is called by physicsRotation to make sure it needs to be cleared
|
|
*/
|
|
native final function CheckDesiredRotation();
|
|
/** IsDesiredRotationInUse()
|
|
* See if DesiredRotation is used by somebody
|
|
*/
|
|
native final function bool IsDesiredRotationInUse();
|
|
/** IsDesiredRotationLocked()
|
|
* See if DesiredRotation is locked by somebody
|
|
*/
|
|
native final function bool IsDesiredRotationLocked();
|
|
|
|
`if(`__TW_)
|
|
native function bool ReachedMyDestination( const out vector TestPosition, const out vector Dest, actor GoalActor );
|
|
`endif
|
|
|
|
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
|
|
{
|
|
Super.PostInitAnimTree(SkelComp);
|
|
|
|
// Only refresh anim nodes if our main mesh was updated
|
|
if (SkelComp == Mesh)
|
|
{
|
|
ClearAnimNodes();
|
|
CacheAnimNodes();
|
|
}
|
|
}
|
|
|
|
/** Save off commonly used nodes so the tree doesn't need to be iterated over often */
|
|
simulated native event CacheAnimNodes();
|
|
|
|
/** Remove references to the saved nodes */
|
|
simulated function ClearAnimNodes()
|
|
{
|
|
SlotNodes.Length = 0;
|
|
}
|
|
|
|
/** Update list of AnimSets for this Pawn */
|
|
simulated native final function UpdateAnimSetList();
|
|
/** Build AnimSet list Script version, called by UpdateAnimSetList() */
|
|
simulated event BuildScriptAnimSetList();
|
|
|
|
/**
|
|
* Add a given list of anim sets on the top of the list (so they override the other ones
|
|
* !! Only use within BuildScriptAnimSetList() !!
|
|
*/
|
|
simulated native final function AddAnimSets(const out array<AnimSet> CustomAnimSets);
|
|
|
|
/** Called after UpdateAnimSetList does its job */
|
|
simulated event AnimSetListUpdated();
|
|
|
|
simulated event bool RestoreAnimSetsToDefault()
|
|
{
|
|
Mesh.AnimSets = default.Mesh.AnimSets;
|
|
return TRUE;
|
|
}
|
|
|
|
// Matinee Track Support Start
|
|
|
|
/** Called when we start an AnimControl track operating on this Actor. Supplied is the set of AnimSets we are going to want to play from. */
|
|
simulated event BeginAnimControl(InterpGroup InInterpGroup)
|
|
{
|
|
// `log(self@" Begin Anim Control : Rotation:"@Rotation@" Location:"@Location);
|
|
MAT_BeginAnimControl(InInterpGroup);
|
|
}
|
|
/** Start AnimControl. Add required AnimSets. */
|
|
native function MAT_BeginAnimControl(InterpGroup InInterpGroup);
|
|
|
|
/** Called when we are done with the AnimControl track. */
|
|
simulated event FinishAnimControl(InterpGroup InInterpGroup)
|
|
{
|
|
MAT_FinishAnimControl(InInterpGroup);
|
|
}
|
|
/** End AnimControl. Release required AnimSets */
|
|
native function MAT_FinishAnimControl(InterpGroup InInterpGroup);
|
|
|
|
/** Called each from while the Matinee action is running, with the desired sequence name and position we want to be at. */
|
|
simulated event SetAnimPosition(name SlotName, int ChannelIndex, name InAnimSeqName, float InPosition, bool bFireNotifies, bool bLooping, bool bEnableRootMotion)
|
|
{
|
|
// `log(self@" Slot:"@SlotName@" AnimSeqName:"@InAnimSeqName@" InPosition:"@InPosition@" Rotation:"@Rotation@" Location:"@Location);
|
|
MAT_SetAnimPosition(SlotName, ChannelIndex, InAnimSeqName, InPosition, bFireNotifies, bLooping, bEnableRootMotion);
|
|
}
|
|
|
|
/** Update AnimTree from track info */
|
|
native function MAT_SetAnimPosition(name SlotName, int ChannelIndex, name InAnimSeqName, float InPosition, bool bFireNotifies, bool bLooping, bool bEnableRootMotion);
|
|
|
|
/** Update AnimTree from track weights */
|
|
native function MAT_SetAnimWeights(Array<AnimSlotInfo> SlotInfos);
|
|
|
|
native function MAT_SetMorphWeight(name MorphNodeName, float MorphWeight);
|
|
|
|
native function MAT_SetSkelControlScale(name SkelControlName, float Scale);
|
|
|
|
native function MAT_SetSkelControlStrength(name SkelControlName, float ControlStrength);
|
|
|
|
/** called when a SeqAct_Interp action starts interpolating this Actor via matinee
|
|
* @note this function is called on clients for actors that are interpolated clientside via MatineeActor
|
|
* @param InterpAction the SeqAct_Interp that is affecting the Actor
|
|
*/
|
|
simulated event InterpolationStarted(SeqAct_Interp InterpAction, InterpGroupInst GroupInst)
|
|
{
|
|
|
|
Super.InterpolationStarted( InterpAction, GroupInst );
|
|
}
|
|
|
|
/** called when a SeqAct_Interp action finished interpolating this Actor
|
|
* @note this function is called on clients for actors that are interpolated clientside via MatineeActor
|
|
* @param InterpAction the SeqAct_Interp that was affecting the Actor
|
|
*/
|
|
simulated event InterpolationFinished(SeqAct_Interp InterpAction)
|
|
{
|
|
Super.InterpolationFinished( InterpAction );
|
|
}
|
|
|
|
simulated function BeginAIGroup();
|
|
simulated function FinishAIGroup();
|
|
|
|
event MAT_BeginAIGroup(vector StartLoc, rotator StartRot)
|
|
{
|
|
SetLocation(StartLoc);
|
|
SetRotation(StartRot);
|
|
BeginAIGroup();
|
|
|
|
bUsedByMatinee = true;
|
|
}
|
|
|
|
event MAT_FinishAIGroup()
|
|
{
|
|
FinishAIGroup();
|
|
bUsedByMatinee = false;
|
|
}
|
|
/**
|
|
* Play FaceFX animations on this Actor.
|
|
* Returns TRUE if succeeded, if failed, a log warning will be issued.
|
|
*/
|
|
// WWISEMODIF_START
|
|
simulated event bool PlayActorFaceFXAnim(FaceFXAnimSet AnimSet, String GroupName, String SeqName, SoundCue SoundCueToPlay, AkEvent AkEventToPlay)
|
|
{
|
|
return Mesh.PlayFaceFXAnim(AnimSet, SeqName, GroupName, SoundCueToPlay, AkEventToPlay);
|
|
}
|
|
// WWISEMODIF_END
|
|
|
|
/** Stop any matinee FaceFX animations on this Actor. */
|
|
event StopActorFaceFXAnim()
|
|
{
|
|
Mesh.StopFaceFXAnim();
|
|
}
|
|
|
|
/** Used to let FaceFX know what component to play dialogue audio on. */
|
|
simulated event AudioComponent GetFaceFXAudioComponent()
|
|
{
|
|
return FacialAudioComp;
|
|
}
|
|
|
|
/**
|
|
* Returns TRUE if Actor is playing a FaceFX anim.
|
|
* Implement in sub-class.
|
|
*/
|
|
simulated function bool IsActorPlayingFaceFXAnim()
|
|
{
|
|
return (Mesh != None && Mesh.IsPlayingFaceFXAnim());
|
|
}
|
|
|
|
/**
|
|
* Returns FALSE??? if Actor can play facefx
|
|
* Implement in sub-class.
|
|
*/
|
|
simulated function bool CanActorPlayFaceFXAnim()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/** Function for handling the SeqAct_PlayFaceFXAnim Kismet action working on this Actor. */
|
|
simulated function OnPlayFaceFXAnim(SeqAct_PlayFaceFXAnim inAction)
|
|
{
|
|
//`log("Play FaceFX animation from KismetAction for" @ Self @ "GroupName:" @ inAction.FaceFXGroupName @ "AnimName:" @ inAction.FaceFXAnimName);
|
|
// WWISEMODIF_START
|
|
Mesh.PlayFaceFXAnim(inAction.FaceFXAnimSetRef, inAction.FaceFXAnimName, inAction.FaceFXGroupName, inAction.SoundCueToPlay, inAction.AkEventToPlay);
|
|
// WWISEMODIF_END
|
|
}
|
|
|
|
/**
|
|
* Called via delegate when FacialAudioComp is finished.
|
|
*/
|
|
simulated function FaceFXAudioFinished(AudioComponent AC)
|
|
{
|
|
}
|
|
|
|
/** Used by Matinee in-game to mount FaceFXAnimSets before playing animations. */
|
|
event FaceFXAsset GetActorFaceFXAsset()
|
|
{
|
|
if (Mesh.SkeletalMesh != None && !Mesh.bDisableFaceFX)
|
|
{
|
|
return Mesh.SkeletalMesh.FaceFXAsset;
|
|
}
|
|
else
|
|
{
|
|
return None;
|
|
}
|
|
}
|
|
|
|
/** Called each frame by Matinee to update the weight of a particular MorphNodeWeight. */
|
|
event SetMorphWeight(name MorphNodeName, float MorphWeight)
|
|
{
|
|
MAT_SetMorphweight(MorphNodeName, MorphWeight);
|
|
}
|
|
|
|
/** Called each frame by Matinee to update the scaling on a SkelControl. */
|
|
event SetSkelControlScale(name SkelControlName, float Scale)
|
|
{
|
|
MAT_SetSkelControlScale(SkelControlName, Scale);
|
|
}
|
|
|
|
// Matinee Track Support End
|
|
|
|
//
|
|
/** Check on various replicated data and act accordingly. */
|
|
simulated event ReplicatedEvent( name VarName )
|
|
{
|
|
//`log( WorldInfo.TimeSeconds @ GetFuncName() @ "VarName:" @ VarName );
|
|
|
|
super.ReplicatedEvent( VarName );
|
|
|
|
if( VarName == 'FlashCount' ) // FlashCount and FlashLocation are changed when a weapon is fired.
|
|
{
|
|
FlashCountUpdated(Weapon, FlashCount, TRUE);
|
|
}
|
|
else if( VarName == 'FlashLocation' ) // FlashCount and FlashLocation are changed when a weapon is fired.
|
|
{
|
|
FlashLocationUpdated(Weapon, FlashLocation, TRUE);
|
|
}
|
|
else if( VarName == 'FiringMode' )
|
|
{
|
|
FiringModeUpdated(Weapon, FiringMode, TRUE);
|
|
}
|
|
else if ( VarName == 'DrivenVehicle' )
|
|
{
|
|
if ( DrivenVehicle != None )
|
|
{
|
|
// since pawn doesn't have a PRI while driving, and may become initially relevant while driving,
|
|
// we may only be able to ascertain the pawn's team (for team coloring, etc.) through its drivenvehicle
|
|
NotifyTeamChanged();
|
|
}
|
|
}
|
|
else if ( VarName == 'PlayerReplicationInfo' )
|
|
{
|
|
NotifyTeamChanged();
|
|
}
|
|
else if ( VarName == 'Controller' )
|
|
{
|
|
if ( (Controller != None) && (Controller.Pawn == None) )
|
|
{
|
|
Controller.Pawn = self;
|
|
if ( (PlayerController(Controller) != None)
|
|
&& (PlayerController(Controller).ViewTarget == Controller) )
|
|
PlayerController(Controller).SetViewTarget(self);
|
|
}
|
|
}
|
|
else if ( VarName == 'bUsedByMatinee')
|
|
{
|
|
if (bUsedByMatinee)
|
|
{
|
|
BeginAIGroup();
|
|
}
|
|
else
|
|
{
|
|
FinishAIGroup();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
|
|
/** Returns TRUE if Pawn is alive and doing well */
|
|
final virtual simulated native function bool IsAliveAndWell() const;
|
|
|
|
final native virtual function Vector AdjustDestination( Actor GoalActor, optional Vector Dest );
|
|
|
|
/** Is the current anchor valid? */
|
|
final native function bool ValidAnchor();
|
|
|
|
/**
|
|
* SuggestJumpVelocity()
|
|
* returns true if succesful jump from start to destination is possible
|
|
* returns a suggested initial falling velocity in JumpVelocity
|
|
* Uses GroundSpeed and JumpZ as limits
|
|
*
|
|
* @param JumpVelocity The vector to fill with the calculated jump velocity
|
|
* @param Destination The destination location of the jump
|
|
* @param Start The start location of the jump
|
|
* @param bRequireFallLanding If true, the jump calculated will have a velocity in the negative Z at the destination
|
|
*/
|
|
native function bool SuggestJumpVelocity(out vector JumpVelocity, vector Destination, vector Start, optional bool bRequireFallLanding);
|
|
|
|
/**
|
|
* GetFallDuration
|
|
* returns time before impact if pawn falls from current position with current velocity
|
|
*/
|
|
native function float GetFallDuration();
|
|
|
|
/** returns if we are a valid enemy for PRI
|
|
* checks things like whether we're alive, teammates, etc
|
|
* works on clients and servers
|
|
*/
|
|
native function bool IsValidEnemyTargetFor(const PlayerReplicationInfo PRI, bool bNoPRIisEnemy);
|
|
|
|
`if(`__TW_)
|
|
/** returns if we are a valid teammate for PRI
|
|
* checks things like whether we're alive, teammates, etc
|
|
* works on clients and servers
|
|
*/
|
|
native function bool IsValidTeamTargetFor(const PlayerReplicationInfo PRI);
|
|
|
|
function bool CanAITargetThisPawn(Controller TargetingController)
|
|
{
|
|
return true;
|
|
}
|
|
`endif
|
|
|
|
/**
|
|
@RETURN true if pawn is invisible to AI
|
|
*/
|
|
native function bool IsInvisible();
|
|
|
|
/**
|
|
* Set Pawn ViewPitch, so we can see where remote clients are looking.
|
|
*
|
|
* @param NewRemoteViewPitch Pitch component to replicate to remote (non owned) clients.
|
|
*/
|
|
native final function SetRemoteViewPitch( int NewRemoteViewPitch );
|
|
|
|
native function SetAnchor( NavigationPoint NewAnchor );
|
|
native function NavigationPoint GetBestAnchor( Actor TestActor, Vector TestLocation, bool bStartPoint, bool bOnlyCheckVisible, out float out_Dist );
|
|
native function bool ReachedDestination(Actor Goal);
|
|
native function bool ReachedPoint( Vector Point, Actor NewAnchor );
|
|
native function ForceCrouch();
|
|
native function SetPushesRigidBodies( bool NewPush );
|
|
native final virtual function bool ReachedDesiredRotation();
|
|
|
|
native function GetBoundingCylinder(out float CollisionRadius, out float CollisionHeight) const;
|
|
|
|
/**
|
|
* Does the following:
|
|
* - Assign the SkeletalMeshComponent 'Mesh' to the CollisionComponent
|
|
* - Call InitArticulated on the SkeletalMeshComponent.
|
|
* - Change the physics mode to PHYS_RigidBody
|
|
*/
|
|
native function bool InitRagdoll();
|
|
/** the opposite of InitRagdoll(); resets CollisionComponent to the default,
|
|
* sets physics to PHYS_Falling, and calls TermArticulated() on the SkeletalMeshComponent
|
|
* @return true on success, false if there is no Mesh, the Mesh is not in ragdoll, or we're otherwise not able to terminate the physics
|
|
*/
|
|
native function bool TermRagdoll();
|
|
|
|
/** Give pawn the chance to do something special moving between points */
|
|
function bool SpecialMoveTo( NavigationPoint Start, NavigationPoint End, Actor Next );
|
|
event bool SpecialMoveThruEdge( ENavMeshEdgeType EdgeType, INT Dir, Vector MoveStart, Vector MoveDest, optional Actor RelActor, optional int RelItem, optional NavigationHandle NavHandle );
|
|
|
|
simulated function SetBaseEyeheight()
|
|
{
|
|
if ( !bIsCrouched )
|
|
BaseEyeheight = Default.BaseEyeheight;
|
|
else
|
|
BaseEyeheight = FMin(0.8 * CrouchHeight, CrouchHeight - 10);
|
|
}
|
|
|
|
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 )
|
|
{
|
|
DetachFromController();
|
|
Destroy();
|
|
}
|
|
else
|
|
super.Reset();
|
|
}
|
|
|
|
function bool StopFiring()
|
|
{
|
|
if( Weapon != None )
|
|
{
|
|
Weapon.StopFire(Weapon.CurrentFireMode);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Pawn starts firing!
|
|
* Called from PlayerController::StartFiring
|
|
* Network: Local Player
|
|
*
|
|
* @param FireModeNum fire mode number
|
|
*/
|
|
simulated function StartFire(byte FireModeNum)
|
|
{
|
|
if( bNoWeaponFIring )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( Weapon != None )
|
|
{
|
|
Weapon.StartFire(FireModeNum);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Pawn stops firing!
|
|
* i.e. player releases fire button, this may not stop weapon firing right away. (for example press button once for a burst fire)
|
|
* Network: Local Player
|
|
*
|
|
* @param FireModeNum fire mode number
|
|
*/
|
|
simulated function StopFire(byte FireModeNum)
|
|
{
|
|
if( Weapon != None )
|
|
{
|
|
Weapon.StopFire(FireModeNum);
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************
|
|
* Remote Client Firing Magic...
|
|
* @See Weapon::IncrementFlashCount()
|
|
********************************************************************************************/
|
|
|
|
/** Return FiringMode currently in use by weapon InWeapon */
|
|
simulated function byte GetWeaponFiringMode(Weapon InWeapon)
|
|
{
|
|
return FiringMode;
|
|
}
|
|
|
|
/**
|
|
* Set firing mode replication for remote clients trigger update notification.
|
|
* Network: LocalPlayer and Server
|
|
*/
|
|
simulated function SetFiringMode(Weapon InWeapon, byte InFiringMode)
|
|
{
|
|
//`log( WorldInfo.TimeSeconds @ GetFuncName() @ "old:" @ FiringMode @ "new:" @ FiringModeNum );
|
|
if( FiringMode != InFiringMode )
|
|
{
|
|
FiringMode = InFiringMode;
|
|
bForceNetUpdate = TRUE;
|
|
|
|
// call updated event locally
|
|
FiringModeUpdated(InWeapon, FiringMode, FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when FiringMode has been updated.
|
|
*
|
|
* Network: ALL
|
|
*/
|
|
simulated function FiringModeUpdated(Weapon InWeapon, byte InFiringMode, bool bViaReplication)
|
|
{
|
|
if( InWeapon != None )
|
|
{
|
|
InWeapon.FireModeUpdated(InFiringMode, bViaReplication);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This function's responsibility is to signal clients that non-instant hit shot
|
|
* has been fired. Call this on the server and local player.
|
|
*
|
|
* Network: Server and Local Player
|
|
*/
|
|
simulated function IncrementFlashCount(Weapon InWeapon, byte InFiringMode)
|
|
{
|
|
bForceNetUpdate = TRUE; // Force replication
|
|
FlashCount++;
|
|
|
|
// Make sure it's not 0, because it means the weapon stopped firing!
|
|
if( FlashCount == 0 )
|
|
{
|
|
FlashCount += 2;
|
|
}
|
|
// Make sure firing mode is updated
|
|
SetFiringMode(InWeapon, InFiringMode);
|
|
|
|
// This weapon has fired.
|
|
FlashCountUpdated(InWeapon, FlashCount, FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Called when FlashCount has been updated.
|
|
* Trigger appropritate events based on FlashCount's value.
|
|
* = 0 means Weapon Stopped firing
|
|
* > 0 means Weapon just fired
|
|
*
|
|
* Network: ALL
|
|
*/
|
|
simulated function FlashCountUpdated(Weapon InWeapon, Byte InFlashCount, bool bViaReplication)
|
|
{
|
|
//`log( WorldInfo.TimeSeconds @ GetFuncName() @ "FlashCount:" @ FlashCount @ "bViaReplication:" @ bViaReplication );
|
|
if( InFlashCount > 0 )
|
|
{
|
|
WeaponFired(InWeapon, bViaReplication);
|
|
}
|
|
else
|
|
{
|
|
WeaponStoppedFiring(InWeapon, bViaReplication);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear flashCount variable. and call WeaponStoppedFiring event.
|
|
* Call this on the server and local player.
|
|
*
|
|
* Network: Server or Local Player
|
|
*/
|
|
simulated function ClearFlashCount(Weapon InWeapon)
|
|
{
|
|
if( FlashCount != 0 )
|
|
{
|
|
bForceNetUpdate = TRUE; // Force replication
|
|
FlashCount = 0;
|
|
|
|
// This weapon stopped firing
|
|
FlashCountUpdated(InWeapon, FlashCount, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This function sets up the Location of a hit to be replicated to all remote clients.
|
|
* It is also responsible for fudging a shot at (0,0,0).
|
|
*
|
|
* Network: Server only (unless using client-side hit detection)
|
|
*/
|
|
|
|
simulated function SetFlashLocation(Weapon InWeapon, byte InFiringMode, vector NewLoc)
|
|
{
|
|
// Make sure 2 consecutive flash locations are different, for replication
|
|
if( NewLoc == LastFiringFlashLocation )
|
|
{
|
|
NewLoc += vect(0,0,1);
|
|
}
|
|
|
|
// If we are aiming at the origin, aim slightly up since we use 0,0,0 to denote
|
|
// not firing.
|
|
if( NewLoc == vect(0,0,0) )
|
|
{
|
|
NewLoc = vect(0,0,1);
|
|
}
|
|
|
|
bForceNetUpdate = TRUE; // Force replication
|
|
FlashLocation = NewLoc;
|
|
LastFiringFlashLocation = NewLoc;
|
|
|
|
// Make sure firing mode is updated
|
|
SetFiringMode(InWeapon, InFiringMode);
|
|
|
|
// This weapon has fired.
|
|
FlashLocationUpdated(InWeapon, FlashLocation, FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Reset flash location variable. and call stop firing.
|
|
* Network: Server only
|
|
*/
|
|
function ClearFlashLocation(Weapon InWeapon)
|
|
{
|
|
if( !IsZero(FlashLocation) )
|
|
{
|
|
bForceNetUpdate = TRUE; // Force replication
|
|
FlashLocation = vect(0,0,0);
|
|
FlashLocationUpdated(InWeapon, FlashLocation, FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when FlashLocation has been updated.
|
|
* Trigger appropritate events based on FlashLocation's value.
|
|
* == (0,0,0) means Weapon Stopped firing
|
|
* != (0,0,0) means Weapon just fired
|
|
*
|
|
* Network: ALL
|
|
*/
|
|
simulated function FlashLocationUpdated(Weapon InWeapon, Vector InFlashLocation, bool bViaReplication)
|
|
{
|
|
//`log( WorldInfo.TimeSeconds @ GetFuncName() @ "FlashLocation:" @ FlashLocation @ "bViaReplication:" @ bViaReplication );
|
|
if( !IsZero(InFlashLocation) )
|
|
{
|
|
WeaponFired(InWeapon, bViaReplication, InFlashLocation);
|
|
}
|
|
else
|
|
{
|
|
WeaponStoppedFiring(InWeapon, bViaReplication);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when a pawn's weapon has fired and is responsibile for
|
|
* delegating the creation of all of the different effects.
|
|
*
|
|
* bViaReplication denotes if this call in as the result of the
|
|
* flashcount/flashlocation being replicated. It's used filter out
|
|
* when to make the effects.
|
|
*
|
|
* Network: ALL
|
|
*/
|
|
simulated function WeaponFired(Weapon InWeapon, bool bViaReplication, optional vector HitLocation)
|
|
{
|
|
// increment number of consecutive shots.
|
|
ShotCount++;
|
|
|
|
// By default we just call PlayFireEffects on the weapon.
|
|
if( InWeapon != None )
|
|
{
|
|
InWeapon.PlayFireEffects(GetWeaponFiringMode(InWeapon), HitLocation);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Called when a pawn's weapon has stopped firing and is responsibile for
|
|
* delegating the destruction of all of the different effects.
|
|
*
|
|
* bViaReplication denotes if this call in as the result of the
|
|
* flashcount/flashlocation being replicated.
|
|
*
|
|
* Network: ALL
|
|
*/
|
|
simulated function WeaponStoppedFiring(Weapon InWeapon, bool bViaReplication)
|
|
{
|
|
// reset number of consecutive shots fired.
|
|
ShotCount = 0;
|
|
|
|
if( InWeapon != None )
|
|
{
|
|
InWeapon.StopFireEffects(GetWeaponFiringMode(InWeapon));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
AI Interface for combat
|
|
**/
|
|
function bool BotFire(bool bFinished)
|
|
{
|
|
StartFire(0);
|
|
return true;
|
|
}
|
|
|
|
function bool CanAttack(Actor Other)
|
|
{
|
|
if ( Weapon == None )
|
|
return false;
|
|
return Weapon.CanAttack(Other);
|
|
}
|
|
|
|
function bool TooCloseToAttack(Actor Other)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function bool FireOnRelease()
|
|
{
|
|
if (Weapon != None)
|
|
return Weapon.FireOnRelease();
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool HasRangedAttack()
|
|
{
|
|
return ( Weapon != None );
|
|
}
|
|
|
|
function bool IsFiring()
|
|
{
|
|
if (Weapon != None)
|
|
return Weapon.IsFiring();
|
|
|
|
return false;
|
|
}
|
|
|
|
/** returns whether we need to turn to fire at the specified location */
|
|
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);
|
|
}
|
|
|
|
simulated function String GetHumanReadableName()
|
|
{
|
|
if ( PlayerReplicationInfo != None )
|
|
return PlayerReplicationInfo.PlayerName;
|
|
return MenuName;
|
|
}
|
|
|
|
function PlayTeleportEffect(bool bOut, bool bSound)
|
|
{
|
|
MakeNoise(1.0);
|
|
}
|
|
|
|
/** NotifyTeamChanged()
|
|
Called when PlayerReplicationInfo is replicated to this pawn, or PlayerReplicationInfo team property changes.
|
|
|
|
Network: client
|
|
*/
|
|
simulated function NotifyTeamChanged();
|
|
|
|
/* PossessedBy()
|
|
Pawn is possessed by Controller
|
|
*/
|
|
function PossessedBy(Controller C, bool bVehicleTransition)
|
|
{
|
|
Controller = C;
|
|
NetPriority = 3;
|
|
NetUpdateFrequency = 100;
|
|
bForceNetUpdate = TRUE;
|
|
|
|
if ( C.PlayerReplicationInfo != None )
|
|
{
|
|
PlayerReplicationInfo = C.PlayerReplicationInfo;
|
|
}
|
|
UpdateControllerOnPossess(bVehicleTransition);
|
|
|
|
SetOwner(Controller); // for network replication
|
|
Eyeheight = BaseEyeHeight;
|
|
|
|
if ( C.IsA('PlayerController') )
|
|
{
|
|
if ( WorldInfo.NetMode != NM_Standalone )
|
|
{
|
|
RemoteRole = ROLE_AutonomousProxy;
|
|
}
|
|
|
|
// inform client of current weapon
|
|
if( Weapon != None )
|
|
{
|
|
Weapon.ClientWeaponSet(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemoteRole = Default.RemoteRole;
|
|
}
|
|
|
|
|
|
//Update the AIController cache
|
|
if (Weapon != None)
|
|
{
|
|
Weapon.CacheAIController();
|
|
}
|
|
}
|
|
|
|
/* UpdateControllerOnPossess()
|
|
update controller - normally, just change its rotation to match pawn rotation
|
|
*/
|
|
function UpdateControllerOnPossess(bool bVehicleTransition)
|
|
{
|
|
// don't set pawn rotation on possess if was driving vehicle, so face
|
|
// same direction when get out as when driving
|
|
if ( !bVehicleTransition )
|
|
{
|
|
Controller.SetRotation(Rotation);
|
|
}
|
|
}
|
|
|
|
function UnPossessed()
|
|
{
|
|
bForceNetUpdate = TRUE;
|
|
if ( DrivenVehicle != None )
|
|
NetUpdateFrequency = 5;
|
|
|
|
PlayerReplicationInfo = None;
|
|
SetOwner(None);
|
|
Controller = None;
|
|
}
|
|
|
|
/**
|
|
* returns default camera mode when viewing this pawn.
|
|
* Mainly called when controller possesses this pawn.
|
|
*
|
|
* @param PlayerController requesting the default camera view
|
|
* @return default camera view player should use when controlling this pawn.
|
|
*/
|
|
simulated function name GetDefaultCameraMode( PlayerController RequestedBy )
|
|
{
|
|
if ( RequestedBy != None && RequestedBy.PlayerCamera != None && RequestedBy.PlayerCamera.CameraStyle == 'Fixed' )
|
|
return 'Fixed';
|
|
|
|
return 'FirstPerson';
|
|
}
|
|
|
|
function DropToGround()
|
|
{
|
|
bCollideWorld = True;
|
|
if ( Health > 0 )
|
|
{
|
|
SetCollision(true,true);
|
|
SetPhysics(PHYS_Falling);
|
|
if ( IsHumanControlled() )
|
|
Controller.GotoState(LandMovementState);
|
|
}
|
|
}
|
|
|
|
function bool CanGrabLadder()
|
|
{
|
|
return ( bCanClimbLadders
|
|
&& (Controller != None)
|
|
&& (Physics != PHYS_Ladder)
|
|
&& ((Physics != Phys_Falling) || (abs(Velocity.Z) <= JumpZ)) );
|
|
}
|
|
|
|
function bool RecommendLongRangedAttack()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function float RangedAttackTime()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Called every frame from PlayerInput or PlayerController::MoveAutonomous()
|
|
* Sets bIsWalking flag, which defines if the Pawn is walking or not (affects velocity)
|
|
*
|
|
* @param bNewIsWalking, new walking state.
|
|
*/
|
|
event SetWalking( bool bNewIsWalking )
|
|
{
|
|
if ( bNewIsWalking != bIsWalking )
|
|
{
|
|
bIsWalking = bNewIsWalking;
|
|
}
|
|
}
|
|
|
|
simulated function bool CanSplash()
|
|
{
|
|
if ( (WorldInfo.TimeSeconds - SplashTime > 0.15)
|
|
&& ((Physics == PHYS_Falling) || (Physics == PHYS_Flying))
|
|
&& (Abs(Velocity.Z) > 100) )
|
|
{
|
|
SplashTime = WorldInfo.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');
|
|
}
|
|
|
|
/**
|
|
* list important Pawn variables on canvas. HUD will call DisplayDebug() on the current ViewTarget when
|
|
* the ShowDebug exec is used
|
|
*
|
|
* @param HUD - HUD with canvas to draw on
|
|
* @input out_YL - Height of the current font
|
|
* @input out_YPos - Y position on Canvas. out_YPos += out_YL, gives position to draw text for next debug line.
|
|
*/
|
|
simulated function DisplayDebug(HUD HUD, out float out_YL, out float out_YPos)
|
|
{
|
|
local string T;
|
|
local Canvas Canvas;
|
|
local AnimTree AnimTreeRootNode;
|
|
local int i;
|
|
|
|
Canvas = HUD.Canvas;
|
|
|
|
if ( PlayerReplicationInfo == None )
|
|
{
|
|
Canvas.DrawText("NO PLAYERREPLICATIONINFO", false);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
}
|
|
else
|
|
{
|
|
PlayerReplicationInfo.DisplayDebug(HUD,out_YL,out_YPos);
|
|
}
|
|
|
|
super.DisplayDebug(HUD, out_YL, out_YPos);
|
|
|
|
Canvas.SetDrawColor(255,255,255);
|
|
|
|
Canvas.DrawText("Health "$Health);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4, out_YPos);
|
|
|
|
if (HUD.ShouldDisplayDebug('AI'))
|
|
{
|
|
Canvas.DrawText("Anchor "$Anchor$" Serpentine Dist "$SerpentineDist$" Time "$SerpentineTime);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
}
|
|
|
|
if (HUD.ShouldDisplayDebug('physics'))
|
|
{
|
|
T = "Floor "$Floor$" DesiredSpeed "$DesiredSpeed$" Crouched "$bIsCrouched;
|
|
if ( (OnLadder != None) || (Physics == PHYS_Ladder) )
|
|
T=T$" on ladder "$OnLadder;
|
|
Canvas.DrawText(T);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
|
|
T = "Collision Component:" @ CollisionComponent;
|
|
Canvas.DrawText(T);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
|
|
T = "bForceMaxAccel:" @ bForceMaxAccel;
|
|
Canvas.DrawText(T);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
|
|
if( Mesh != None )
|
|
{
|
|
T = "RootMotionMode:" @ Mesh.RootMotionMode @ "RootMotionVelocity:" @ Mesh.RootMotionVelocity;
|
|
Canvas.DrawText(T);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
}
|
|
}
|
|
|
|
if (HUD.ShouldDisplayDebug('camera'))
|
|
{
|
|
Canvas.DrawText("EyeHeight "$Eyeheight$" BaseEyeHeight "$BaseEyeHeight);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
}
|
|
|
|
// Controller
|
|
if ( Controller == None )
|
|
{
|
|
Canvas.SetDrawColor(255,0,0);
|
|
Canvas.DrawText("NO CONTROLLER");
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
HUD.PlayerOwner.DisplayDebug(HUD, out_YL, out_YPos);
|
|
}
|
|
else
|
|
{
|
|
Controller.DisplayDebug(HUD, out_YL, out_YPos);
|
|
}
|
|
|
|
// Weapon
|
|
if (HUD.ShouldDisplayDebug('weapon'))
|
|
{
|
|
if ( Weapon == None )
|
|
{
|
|
Canvas.SetDrawColor(0,255,0);
|
|
Canvas.DrawText("NO WEAPON");
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4, out_YPos);
|
|
}
|
|
else
|
|
Weapon.DisplayDebug(HUD, out_YL, out_YPos);
|
|
}
|
|
|
|
if( HUD.ShouldDisplayDebug('animation') )
|
|
{
|
|
if( Mesh != None && Mesh.Animations != None )
|
|
{
|
|
AnimTreeRootNode = AnimTree(Mesh.Animations);
|
|
if( AnimTreeRootNode != None )
|
|
{
|
|
Canvas.DrawText("AnimGroups count:" @ AnimTreeRootNode.AnimGroups.Length);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
|
|
for(i=0; i<AnimTreeRootNode.AnimGroups.Length; i++)
|
|
{
|
|
Canvas.DrawText(" GroupName:" @ AnimTreeRootNode.AnimGroups[i].GroupName @ "NodeCount:" @ AnimTreeRootNode.AnimGroups[i].SeqNodes.Length @ "RateScale:" @ AnimTreeRootNode.AnimGroups[i].RateScale);
|
|
out_YPos += out_YL;
|
|
Canvas.SetPos(4,out_YPos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//***************************************
|
|
// Interface to Pawn's Controller
|
|
|
|
/**
|
|
* IsHumanControlled()
|
|
* @param PawnController - optional parameter so you can pass a controller that is associated with this pawn but is not attached to it
|
|
* @return - true if controlled by a real live human on the local machine. On client, only local player's pawn returns true
|
|
*/
|
|
simulated final native function bool IsHumanControlled(optional Controller PawnController);
|
|
|
|
/**
|
|
* @return - true if controlled by local (not network) player
|
|
*/
|
|
simulated native final function bool IsLocallyControlled();
|
|
|
|
/** IsPlayerPawn()
|
|
return true if controlled by a Player (AI or human) on local machine (any controller on server, localclient's pawn on client)
|
|
*/
|
|
simulated native function bool IsPlayerPawn() const;
|
|
|
|
// 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.UsingFirstPersonCamera() );
|
|
}
|
|
|
|
/**
|
|
* Called from PlayerController UpdateRotation() -> ProcessViewRotation() to (pre)process player ViewRotation
|
|
* adds delta rot (player input), applies any limits and post-processing
|
|
* returns the final ViewRotation set on PlayerController
|
|
*
|
|
* @param DeltaTime, time since last frame
|
|
* @param ViewRotation, actual PlayerController view rotation
|
|
* @input out_DeltaRot, delta rotation to be applied on ViewRotation. Represents player's input.
|
|
* @return processed ViewRotation to be set on PlayerController.
|
|
*/
|
|
simulated function ProcessViewRotation( float DeltaTime, out rotator out_ViewRotation, out Rotator out_DeltaRot )
|
|
{
|
|
// Add Delta Rotation
|
|
out_ViewRotation += out_DeltaRot;
|
|
out_DeltaRot = rot(0,0,0);
|
|
|
|
// Limit Player View Pitch
|
|
if ( PlayerController(Controller) != None )
|
|
{
|
|
out_ViewRotation = PlayerController(Controller).LimitViewRotation( out_ViewRotation, ViewPitchMin, ViewPitchMax );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* returns the point of view of the actor.
|
|
* note that this doesn't mean the camera, but the 'eyes' of the actor.
|
|
* For example, for a Pawn, this would define the eye height location,
|
|
* and view rotation (which is different from the pawn rotation which has a zeroed pitch component).
|
|
* A camera first person view will typically use this view point. Most traces (weapon, AI) will be done from this view point.
|
|
*
|
|
* @output out_Location, location of view point
|
|
* @output out_Rotation, view rotation of actor.
|
|
*/
|
|
simulated event GetActorEyesViewPoint( out vector out_Location, out Rotator out_Rotation )
|
|
{
|
|
out_Location = GetPawnViewLocation();
|
|
out_Rotation = GetViewRotation();
|
|
}
|
|
|
|
/** @return the rotation the Pawn is looking
|
|
*/
|
|
simulated native event rotator GetViewRotation();
|
|
|
|
/**
|
|
* returns the Eye location of the Pawn.
|
|
*
|
|
* @return Pawn's eye location
|
|
*/
|
|
simulated native event vector GetPawnViewLocation();
|
|
|
|
/**
|
|
* Return world location to start a weapon fire trace from.
|
|
*
|
|
* @return World location where to start weapon fire traces from
|
|
*/
|
|
simulated event Vector GetWeaponStartTraceLocation(optional Weapon CurrentWeapon)
|
|
{
|
|
local vector POVLoc;
|
|
local rotator POVRot;
|
|
|
|
// If we have a controller, by default we start tracing from the player's 'eyes' location
|
|
// that is by default Controller.Location for AI, and camera (crosshair) location for human players.
|
|
if ( Controller != None )
|
|
{
|
|
Controller.GetPlayerViewPoint( POVLoc, POVRot );
|
|
return POVLoc;
|
|
}
|
|
|
|
// If we have no controller, we simply traces from pawn eyes location
|
|
return GetPawnViewLocation();
|
|
}
|
|
|
|
|
|
/**
|
|
* returns base Aim Rotation without any adjustment (no aim error, no autolock, no adhesion.. just clean initial aim rotation!)
|
|
*
|
|
* @return base Aim rotation.
|
|
*/
|
|
simulated singular event Rotator GetBaseAimRotation()
|
|
{
|
|
local vector POVLoc;
|
|
local rotator POVRot;
|
|
|
|
// If we have a controller, by default we aim at the player's 'eyes' direction
|
|
// that is by default Controller.Rotation for AI, and camera (crosshair) rotation for human players.
|
|
if( Controller != None && !InFreeCam() )
|
|
{
|
|
Controller.GetPlayerViewPoint(POVLoc, POVRot);
|
|
return POVRot;
|
|
}
|
|
|
|
// If we have no controller, we simply use our rotation
|
|
POVRot = Rotation;
|
|
|
|
// If our Pitch is 0, then use RemoveViewPitch
|
|
if( POVRot.Pitch == 0 )
|
|
{
|
|
POVRot.Pitch = RemoteViewPitch << 8;
|
|
}
|
|
|
|
return POVRot;
|
|
}
|
|
|
|
/** return true if player is viewing this Pawn in FreeCam */
|
|
simulated event bool InFreeCam()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
PC = PlayerController(Controller);
|
|
return (PC != None && PC.PlayerCamera != None && (PC.PlayerCamera.CameraStyle == 'FreeCam' || PC.PlayerCamera.CameraStyle == 'FreeCam_Default') );
|
|
}
|
|
|
|
/**
|
|
* Adjusts weapon aiming direction.
|
|
* Gives Pawn a chance to modify its aiming. For example aim error, auto aiming, adhesion, AI help...
|
|
* Requested by weapon prior to firing.
|
|
*
|
|
* @param W, weapon about to fire
|
|
* @param StartFireLoc, world location of weapon fire start trace, or projectile spawn loc.
|
|
* @param BaseAimRot, original aiming rotation without any modifications.
|
|
*/
|
|
simulated function Rotator GetAdjustedAimFor( Weapon W, vector StartFireLoc )
|
|
{
|
|
// If controller doesn't exist or we're a client, get the where the Pawn is aiming at
|
|
if ( Controller == None || Role < Role_Authority )
|
|
{
|
|
return GetBaseAimRotation();
|
|
}
|
|
|
|
// otherwise, give a chance to controller to adjust this Aim Rotation
|
|
return Controller.GetAdjustedAimFor( W, StartFireLoc );
|
|
}
|
|
|
|
simulated function SetViewRotation(rotator NewRotation )
|
|
{
|
|
if (Controller != None)
|
|
{
|
|
Controller.SetRotation(NewRotation);
|
|
}
|
|
else
|
|
{
|
|
SetRotation(NewRotation);
|
|
}
|
|
}
|
|
|
|
function bool InGodMode()
|
|
{
|
|
return ( (Controller != None) && Controller.bGodMode );
|
|
}
|
|
|
|
function SetMoveTarget(Actor NewTarget )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.MoveTarget = NewTarget;
|
|
}
|
|
|
|
function bool LineOfSightTo(actor Other)
|
|
{
|
|
return ( (Controller != None) && Controller.LineOfSightTo(Other) );
|
|
}
|
|
|
|
function HandlePickup(Inventory Inv)
|
|
{
|
|
MakeNoise(0.2);
|
|
if ( Controller != None )
|
|
Controller.HandlePickup(Inv);
|
|
}
|
|
|
|
event ClientMessage( coerce string S, optional Name Type )
|
|
{
|
|
if ( PlayerController(Controller) != None )
|
|
PlayerController(Controller).ClientMessage( S, Type );
|
|
}
|
|
|
|
simulated event FellOutOfWorld(class<DamageType> dmgType)
|
|
{
|
|
if ( Role == ROLE_Authority )
|
|
{
|
|
Health = -1;
|
|
Died( None, dmgType, Location );
|
|
if ( dmgType == None )
|
|
{
|
|
SetPhysics(PHYS_None);
|
|
SetHidden(True);
|
|
LifeSpan = FMin(LifeSpan, 1.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated singular event OutsideWorldBounds()
|
|
{
|
|
// AI pawns on the server just destroy
|
|
if (Role == ROLE_Authority && PlayerController(Controller) == None)
|
|
{
|
|
Destroy();
|
|
}
|
|
else
|
|
{
|
|
// simply destroying the Pawn could cause synchronization issues with the client controlling it
|
|
// so kill it, disable it, and wait a while to give it time to replicate before destroying it
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
KilledBy(self);
|
|
}
|
|
SetPhysics(PHYS_None);
|
|
SetHidden(True);
|
|
LifeSpan = FMin(LifeSpan, 1.0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes sure a Pawn is not crouching, telling it to stand if necessary.
|
|
*/
|
|
simulated function UnCrouch()
|
|
{
|
|
if( bIsCrouched || bWantsToCrouch )
|
|
{
|
|
ShouldCrouch( false );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Controller is requesting that pawn crouches.
|
|
* This is not guaranteed as it depends if crouching collision cylinder can fit when Pawn is located.
|
|
*
|
|
* @param bCrouch true if Pawn should crouch.
|
|
*/
|
|
function ShouldCrouch( bool bCrouch )
|
|
{
|
|
bWantsToCrouch = bCrouch;
|
|
}
|
|
|
|
/**
|
|
* Event called from native code when Pawn stops crouching.
|
|
* Called on non owned Pawns through bIsCrouched replication.
|
|
* Network: ALL
|
|
*
|
|
* @param HeightAdjust height difference in unreal units between default collision height, and actual crouched cylinder height.
|
|
*/
|
|
simulated event EndCrouch( float HeightAdjust )
|
|
{
|
|
EyeHeight -= HeightAdjust;
|
|
SetBaseEyeHeight();
|
|
}
|
|
|
|
/**
|
|
* Event called from native code when Pawn starts crouching.
|
|
* Called on non owned Pawns through bIsCrouched replication.
|
|
* Network: ALL
|
|
*
|
|
* @param HeightAdjust height difference in unreal units between default collision height, and actual crouched cylinder height.
|
|
*/
|
|
simulated event StartCrouch( float HeightAdjust )
|
|
{
|
|
EyeHeight += HeightAdjust;
|
|
SetBaseEyeHeight();
|
|
}
|
|
|
|
function HandleMomentum( vector Momentum, Vector HitLocation, class<DamageType> DamageType, optional TraceHitInfo HitInfo )
|
|
{
|
|
AddVelocity( Momentum, HitLocation, DamageType, HitInfo );
|
|
}
|
|
|
|
function AddVelocity( vector NewVelocity, vector HitLocation, class<DamageType> damageType, optional TraceHitInfo HitInfo )
|
|
{
|
|
if ( bIgnoreForces || (NewVelocity == vect(0,0,0)) )
|
|
return;
|
|
if ( (Physics == PHYS_Walking)
|
|
|| (((Physics == PHYS_Ladder) || (Physics == PHYS_Spider)) && (NewVelocity.Z > Default.JumpZ)) )
|
|
SetPhysics(PHYS_Falling);
|
|
if ( (Velocity.Z > Default.JumpZ) && (NewVelocity.Z > 0) )
|
|
NewVelocity.Z *= 0.5;
|
|
Velocity += NewVelocity;
|
|
}
|
|
|
|
function KilledBy( pawn EventInstigator )
|
|
{
|
|
local Controller Killer;
|
|
|
|
Health = 0;
|
|
if ( EventInstigator != None )
|
|
{
|
|
Killer = EventInstigator.Controller;
|
|
LastHitBy = None;
|
|
}
|
|
Died( Killer, class'DmgType_Suicided', Location );
|
|
}
|
|
|
|
function TakeFallingDamage()
|
|
{
|
|
local float 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 += 100;
|
|
}
|
|
if (EffectiveSpeed < -1 * MaxFallSpeed)
|
|
{
|
|
TakeDamage(-100 * (EffectiveSpeed + MaxFallSpeed)/MaxFallSpeed, None, Location, vect(0,0,0), class'DmgType_Fell');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (Velocity.Z < -1.4 * JumpZ)
|
|
MakeNoise(0.5);
|
|
else if ( Velocity.Z < -0.8 * JumpZ )
|
|
MakeNoise(0.2);
|
|
}
|
|
|
|
function Restart();
|
|
|
|
simulated function ClientReStart()
|
|
{
|
|
ZeroMovementVariables();
|
|
SetBaseEyeHeight();
|
|
}
|
|
|
|
function ClientSetRotation( rotator NewRotation )
|
|
{
|
|
if ( Controller != None )
|
|
Controller.ClientSetRotation(NewRotation);
|
|
}
|
|
|
|
/** Script function callable from C++ to update the Pawn's rotation, and goes through the FaceRotation logic to apply rotation constraints */
|
|
final event simulated UpdatePawnRotation(Rotator NewRotation)
|
|
{
|
|
FaceRotation(NewRotation, 0.f);
|
|
}
|
|
|
|
simulated function FaceRotation(rotator NewRotation, float DeltaTime)
|
|
{
|
|
// Do not update Pawn's rotation depending on controller's ViewRotation if in FreeCam.
|
|
if (!InFreeCam())
|
|
{
|
|
if ( Physics == PHYS_Ladder )
|
|
{
|
|
NewRotation = OnLadder.Walldir;
|
|
}
|
|
else if ( (Physics == PHYS_Walking) || (Physics == PHYS_Falling) )
|
|
{
|
|
NewRotation.Pitch = 0;
|
|
}
|
|
|
|
SetRotation(NewRotation);
|
|
}
|
|
}
|
|
|
|
//==============
|
|
// Encroachment
|
|
event bool EncroachingOn( actor Other )
|
|
{
|
|
if ( Other.bWorldGeometry || Other.bBlocksTeleport )
|
|
return true;
|
|
|
|
if ( ((Controller == None) || !Controller.bIsPlayer) && (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'DmgType_Telefragged', Location);
|
|
else
|
|
Died(None, class'DmgType_Telefragged', 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 + CylinderComponent.CollisionRadius) * VRand();
|
|
if ( VSize2D(Velocity) > FMax(500.0, GroundSpeed) )
|
|
{
|
|
Velocity = FMax(500.0, GroundSpeed) * Normal(Velocity);
|
|
}
|
|
Velocity.Z = 200 + CylinderComponent.CollisionHeight;
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
|
|
/** Called when pawn cylinder embedded in another pawn. (Collision bug that needs to be fixed).
|
|
*/
|
|
event StuckOnPawn(Pawn OtherPawn);
|
|
|
|
/**
|
|
* Event called after actor's base changes.
|
|
*/
|
|
singular event BaseChange()
|
|
{
|
|
local DynamicSMActor Dyn;
|
|
|
|
// Pawns can only set base to non-pawns, or pawns which specifically allow it.
|
|
// Otherwise we do some damage and jump off.
|
|
if (Pawn(Base) != None && (DrivenVehicle == None || !DrivenVehicle.IsBasedOn(Base)))
|
|
{
|
|
if( !Pawn(Base).CanBeBaseForPawn(Self) )
|
|
{
|
|
Pawn(Base).CrushedBy(self);
|
|
JumpOffPawn();
|
|
}
|
|
}
|
|
|
|
// If it's a KActor, see if we can stand on it.
|
|
Dyn = DynamicSMActor(Base);
|
|
if( Dyn != None && !Dyn.CanBasePawn(self) )
|
|
|
|
{
|
|
JumpOffPawn();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Are we allowing this Pawn to be based on us?
|
|
*/
|
|
simulated function bool CanBeBaseForPawn(Pawn APawn)
|
|
{
|
|
return bCanBeBaseForPawns;
|
|
}
|
|
|
|
/** CrushedBy()
|
|
Called for pawns that have bCanBeBaseForPawns=false when another pawn becomes based on them
|
|
*/
|
|
function CrushedBy(Pawn OtherPawn)
|
|
{
|
|
TakeDamage( (1-OtherPawn.Velocity.Z/400)* OtherPawn.Mass/Mass, OtherPawn.Controller,Location, vect(0,0,0) , class'DmgType_Crushed');
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Call this function to detach safely pawn from its controller
|
|
*
|
|
* @param bDestroyController if true, then destroy controller. (only AI Controllers, not players)
|
|
*/
|
|
function DetachFromController( optional bool bDestroyController )
|
|
{
|
|
local Controller OldController;
|
|
|
|
// if we have a controller, notify it we're getting destroyed
|
|
// be careful with bTearOff, we're authority on client! Make sure our controller and pawn match up.
|
|
if ( Controller != None && Controller.Pawn == Self )
|
|
{
|
|
OldController = Controller;
|
|
Controller.PawnDied( Self );
|
|
if ( Controller != None )
|
|
{
|
|
Controller.UnPossess();
|
|
}
|
|
|
|
if ( bDestroyController && OldController != None && !OldController.bDeleteMe && !OldController.bIsPlayer )
|
|
{
|
|
OldController.Destroy();
|
|
}
|
|
Controller = None;
|
|
}
|
|
}
|
|
|
|
simulated event Destroyed()
|
|
{
|
|
DetachFromController();
|
|
|
|
if ( InvManager != None )
|
|
InvManager.Destroy();
|
|
|
|
if ( WorldInfo.NetMode == NM_Client )
|
|
return;
|
|
|
|
// Clear anchor to avoid checkpoint crash
|
|
SetAnchor( None );
|
|
|
|
Weapon = None;
|
|
|
|
//debug
|
|
ClearPathStep();
|
|
|
|
super.Destroyed();
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Called immediately before gameplay begins.
|
|
//
|
|
simulated event PreBeginPlay()
|
|
{
|
|
// important that this comes before Super so mutators can modify it
|
|
if (HealthMax == 0)
|
|
{
|
|
HealthMax = default.Health;
|
|
}
|
|
|
|
Super.PreBeginPlay();
|
|
|
|
Instigator = self;
|
|
SetDesiredRotation(Rotation);
|
|
EyeHeight = BaseEyeHeight;
|
|
}
|
|
|
|
event PostBeginPlay()
|
|
{
|
|
super.PostBeginPlay();
|
|
|
|
SplashTime = 0;
|
|
SpawnTime = WorldInfo.TimeSeconds;
|
|
EyeHeight = BaseEyeHeight;
|
|
|
|
// automatically add controller to pawns which were placed in level
|
|
// NOTE: pawns spawned during gameplay are not automatically possessed by a controller
|
|
if ( WorldInfo.bStartup && (Health > 0) && !bDontPossess )
|
|
{
|
|
SpawnDefaultController();
|
|
}
|
|
|
|
if( FacialAudioComp != None )
|
|
{
|
|
FacialAudioComp.OnAudioFinished = FaceFXAudioFinished;
|
|
}
|
|
|
|
// Spawn Inventory Container
|
|
if (Role == ROLE_Authority && InvManager == None && InventoryManagerClass != None)
|
|
{
|
|
InvManager = Spawn(InventoryManagerClass, Self);
|
|
if ( InvManager == None )
|
|
`log("Warning! Couldn't spawn InventoryManager" @ InventoryManagerClass @ "for" @ Self @ GetHumanReadableName() );
|
|
else
|
|
InvManager.SetupFor( Self );
|
|
}
|
|
|
|
//debug
|
|
ClearPathStep();
|
|
}
|
|
|
|
|
|
/**
|
|
* Spawn default controller for this Pawn, get possessed by it.
|
|
*/
|
|
function SpawnDefaultController()
|
|
{
|
|
if ( Controller != None )
|
|
{
|
|
`log("SpawnDefaultController" @ Self @ ", Controller != None" @ Controller );
|
|
return;
|
|
}
|
|
|
|
if ( ControllerClass != None )
|
|
{
|
|
Controller = Spawn(ControllerClass);
|
|
}
|
|
|
|
if ( Controller != None )
|
|
{
|
|
Controller.Possess( Self, false );
|
|
}
|
|
}
|
|
|
|
simulated event ReceivedNewEvent(SequenceEvent Evt)
|
|
{
|
|
if (Controller != None)
|
|
{
|
|
Controller.ReceivedNewEvent(Evt);
|
|
}
|
|
Super.ReceivedNewEvent(Evt);
|
|
}
|
|
|
|
/**
|
|
* Deletes the current controller if it exists and creates a new one
|
|
* using the specified class.
|
|
* Event called from Kismet.
|
|
*
|
|
* @param inAction - scripted action that was activated
|
|
*/
|
|
function OnAssignController(SeqAct_AssignController inAction)
|
|
{
|
|
|
|
if ( inAction.ControllerClass != None )
|
|
{
|
|
if ( Controller != None )
|
|
{
|
|
DetachFromController( true );
|
|
}
|
|
|
|
Controller = Spawn(inAction.ControllerClass);
|
|
Controller.Possess( Self, false );
|
|
|
|
// Set class as the default one if pawn is restarted.
|
|
if ( Controller.IsA('AIController') )
|
|
{
|
|
ControllerClass = class<AIController>(Controller.Class);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
`warn("Assign controller w/o a class specified!");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Iterates through the list of item classes specified in the action
|
|
* and creates instances that are addeed to this Pawn's inventory.
|
|
*
|
|
* @param inAction - scripted action that was activated
|
|
*/
|
|
simulated function OnGiveInventory(SeqAct_GiveInventory InAction)
|
|
{
|
|
local int Idx;
|
|
local class<Inventory> InvClass;
|
|
|
|
if (InAction.bClearExisting)
|
|
{
|
|
InvManager.DiscardInventory();
|
|
}
|
|
|
|
if (InAction.InventoryList.Length > 0 )
|
|
{
|
|
for (Idx = 0; Idx < InAction.InventoryList.Length; Idx++)
|
|
{
|
|
InvClass = InAction.InventoryList[idx];
|
|
if (InvClass != None)
|
|
{
|
|
// only create if it doesn't already exist
|
|
if (FindInventoryType(InvClass,FALSE) == None)
|
|
{
|
|
CreateInventory(InvClass);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InAction.ScriptLog("WARNING: Attempting to give NULL inventory!");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InAction.ScriptLog("WARNING: Give Inventory without any inventory specified!");
|
|
}
|
|
}
|
|
|
|
function Gasp();
|
|
|
|
function SetMovementPhysics()
|
|
{
|
|
// check for water volume
|
|
if (PhysicsVolume.bWaterVolume)
|
|
{
|
|
SetPhysics(PHYS_Swimming);
|
|
}
|
|
else if (Physics != PHYS_Falling)
|
|
{
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
}
|
|
|
|
/* AdjustDamage()
|
|
adjust damage based on inventory, other attributes
|
|
*/
|
|
function AdjustDamage(out int InDamage, out vector Momentum, Controller InstigatedBy, vector HitLocation, class<DamageType> DamageType, TraceHitInfo HitInfo, Actor DamageCauser);
|
|
|
|
`if(`__TW_)
|
|
event bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType, optional bool bCanRepairArmor=true, optional bool bMessageHealer=true)
|
|
`else
|
|
event bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType)
|
|
`endif
|
|
{
|
|
// not if already dead or already at full
|
|
if (Health > 0 && Health < HealthMax)
|
|
{
|
|
Health = Min(HealthMax, Health + Amount);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** Take a list of bones passed to TakeRadiusDamageOnBones and remove the ones that don't matter */
|
|
function PruneDamagedBoneList( out array<Name> Bones );
|
|
|
|
/**
|
|
* Damage radius applied to specific bones on the skeletal mesh
|
|
*/
|
|
event bool TakeRadiusDamageOnBones
|
|
(
|
|
Controller InstigatedBy,
|
|
float BaseDamage,
|
|
float DamageRadius,
|
|
class<DamageType> DamageType,
|
|
float Momentum,
|
|
vector HurtOrigin,
|
|
bool bFullDamage,
|
|
Actor DamageCauser,
|
|
array<Name> Bones
|
|
)
|
|
{
|
|
|
|
local int Idx;
|
|
local TraceHitInfo HitInfo;
|
|
local bool bResult;
|
|
local float DamageScale, Dist;
|
|
local vector Dir, BoneLoc;
|
|
|
|
PruneDamagedBoneList( Bones );
|
|
|
|
for( Idx = 0; Idx < Bones.Length; Idx++ )
|
|
{
|
|
HitInfo.BoneName = Bones[Idx];
|
|
HitInfo.HitComponent = Mesh;
|
|
|
|
BoneLoc = Mesh.GetBoneLocation(Bones[Idx]);
|
|
Dir = BoneLoc - HurtOrigin;
|
|
Dist = VSize(Dir);
|
|
Dir = Normal(Dir);
|
|
if( bFullDamage )
|
|
{
|
|
DamageScale = 1.f;
|
|
}
|
|
else
|
|
{
|
|
DamageScale = 1.f - Dist/DamageRadius;
|
|
}
|
|
|
|
if( DamageScale > 0.f )
|
|
{
|
|
TakeDamage
|
|
(
|
|
DamageScale * BaseDamage,
|
|
InstigatedBy,
|
|
BoneLoc,
|
|
DamageScale * Momentum * Dir,
|
|
DamageType,
|
|
HitInfo,
|
|
DamageCauser
|
|
);
|
|
}
|
|
|
|
bResult = TRUE;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/** sends any notifications to anything that needs to know this pawn has taken damage */
|
|
function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> DamageType, vector Momentum, Actor DamageCauser)
|
|
{
|
|
if (Controller != None)
|
|
{
|
|
Controller.NotifyTakeHit(InstigatedBy, HitLocation, Damage, DamageType, Momentum);
|
|
}
|
|
}
|
|
|
|
function controller SetKillInstigator(Controller InstigatedBy, class<DamageType> DamageType)
|
|
{
|
|
if ( (InstigatedBy != None) && (InstigatedBy != Controller) )
|
|
{
|
|
return InstigatedBy;
|
|
}
|
|
else if ( DamageType.default.bCausedByWorld && (LastHitBy != None) )
|
|
{
|
|
return LastHitBy;
|
|
}
|
|
return InstigatedBy;
|
|
}
|
|
|
|
event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
|
|
{
|
|
local int actualDamage;
|
|
local PlayerController PC;
|
|
local Controller Killer;
|
|
|
|
if ( (Role < ROLE_Authority) || (Health <= 0) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( damagetype == None )
|
|
{
|
|
/*if ( InstigatedBy == None )
|
|
`warn("No damagetype for damage with no instigator");
|
|
else
|
|
`warn("No damagetype for damage by "$instigatedby.pawn$" with weapon "$InstigatedBy.Pawn.Weapon);
|
|
*/
|
|
//scripttrace();
|
|
DamageType = class'DamageType';
|
|
}
|
|
Damage = Max(Damage, 0);
|
|
|
|
if (Physics == PHYS_None && DrivenVehicle == None)
|
|
{
|
|
SetMovementPhysics();
|
|
}
|
|
if (Physics == PHYS_Walking && damageType.default.bExtraMomentumZ)
|
|
{
|
|
momentum.Z = FMax(momentum.Z, 0.4 * VSize(momentum));
|
|
}
|
|
momentum = momentum/Mass;
|
|
|
|
if ( DrivenVehicle != None )
|
|
{
|
|
DrivenVehicle.AdjustDriverDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType );
|
|
}
|
|
|
|
ActualDamage = Damage;
|
|
`if(`__TW_)
|
|
WorldInfo.Game.ReduceDamage(ActualDamage, self, instigatedBy, HitLocation, Momentum, DamageType, DamageCauser, HitInfo);
|
|
`else
|
|
WorldInfo.Game.ReduceDamage(ActualDamage, self, instigatedBy, HitLocation, Momentum, DamageType, DamageCauser);
|
|
`endif
|
|
AdjustDamage(ActualDamage, Momentum, instigatedBy, HitLocation, DamageType, HitInfo, DamageCauser);
|
|
|
|
// call Actor's version to handle any SeqEvent_TakeDamage for scripting
|
|
Super.TakeDamage(ActualDamage, InstigatedBy, HitLocation, Momentum, DamageType, HitInfo, DamageCauser);
|
|
|
|
Health -= actualDamage;
|
|
if (HitLocation == vect(0,0,0))
|
|
{
|
|
HitLocation = Location;
|
|
}
|
|
|
|
if ( Health <= 0 )
|
|
{
|
|
PC = PlayerController(Controller);
|
|
// play force feedback for death
|
|
if (PC != None)
|
|
{
|
|
PC.ClientPlayForceFeedbackWaveform(damageType.default.KilledFFWaveform);
|
|
}
|
|
// pawn died
|
|
Killer = SetKillInstigator(InstigatedBy, DamageType);
|
|
TearOffMomentum = momentum;
|
|
Died(Killer, damageType, HitLocation);
|
|
}
|
|
else
|
|
{
|
|
HandleMomentum( momentum, HitLocation, DamageType, HitInfo );
|
|
NotifyTakeHit(InstigatedBy, HitLocation, ActualDamage, DamageType, Momentum, DamageCauser);
|
|
if (DrivenVehicle != None)
|
|
{
|
|
DrivenVehicle.NotifyDriverTakeHit(InstigatedBy, HitLocation, actualDamage, DamageType, Momentum);
|
|
}
|
|
if ( instigatedBy != None && instigatedBy != controller )
|
|
{
|
|
LastHitBy = instigatedBy;
|
|
}
|
|
}
|
|
PlayHit(actualDamage,InstigatedBy, hitLocation, damageType, Momentum, HitInfo);
|
|
`if(`__TW_)
|
|
// Best to use a noise label whenever possible to make it easier for the AI to identify what they just "heard"
|
|
MakeNoise(1.0, 'TakeDamage');
|
|
`else
|
|
MakeNoise(1.0);
|
|
`endif
|
|
}
|
|
|
|
/*
|
|
* Queries the PRI and returns our current team index.
|
|
*/
|
|
simulated native function byte GetTeamNum();
|
|
|
|
|
|
simulated function TeamInfo GetTeam()
|
|
{
|
|
if (Controller != None && Controller.PlayerReplicationInfo != None)
|
|
{
|
|
return Controller.PlayerReplicationInfo.Team;
|
|
}
|
|
else if (PlayerReplicationInfo != None)
|
|
{
|
|
return PlayerReplicationInfo.Team;
|
|
}
|
|
else if (DrivenVehicle != None && DrivenVehicle.PlayerReplicationInfo != None)
|
|
{
|
|
return DrivenVehicle.PlayerReplicationInfo.Team;
|
|
}
|
|
else
|
|
{
|
|
return None;
|
|
}
|
|
}
|
|
|
|
/** Returns true of pawns are on the same team, false otherwise */
|
|
simulated event bool IsSameTeam( Pawn Other )
|
|
{
|
|
return ( Other != None &&
|
|
Other.GetTeam() != None &&
|
|
Other.GetTeam() == GetTeam() );
|
|
}
|
|
|
|
/** called to throw any weapon(s) that should be thrown on death */
|
|
function ThrowWeaponOnDeath()
|
|
{
|
|
ThrowActiveWeapon();
|
|
}
|
|
|
|
/**
|
|
* This pawn has died.
|
|
*
|
|
* @param Killer Who killed this pawn
|
|
* @param DamageType What killed it
|
|
* @param HitLocation Where did the hit occur
|
|
*
|
|
* @returns true if allowed
|
|
*/
|
|
function bool Died(Controller Killer, class<DamageType> DamageType, vector HitLocation)
|
|
{
|
|
local SeqAct_Latent Action;
|
|
|
|
// ensure a valid damagetype
|
|
if ( damageType == None )
|
|
{
|
|
damageType = class'DamageType';
|
|
}
|
|
// if already destroyed or level transition is occuring then ignore
|
|
if ( bDeleteMe || WorldInfo.Game == None || WorldInfo.Game.bLevelChange )
|
|
{
|
|
return FALSE;
|
|
}
|
|
// if this is an environmental death then refer to the previous killer so that they receive credit (knocked into lava pits, etc)
|
|
if ( DamageType.default.bCausedByWorld && (Killer == None || Killer == Controller) && LastHitBy != None )
|
|
{
|
|
Killer = LastHitBy;
|
|
}
|
|
// gameinfo hook to prevent deaths
|
|
// WARNING - don't prevent bot suicides - they suicide when really needed
|
|
if ( WorldInfo.Game.PreventDeath(self, Killer, damageType, HitLocation) )
|
|
{
|
|
Health = max(Health, 1);
|
|
return false;
|
|
}
|
|
Health = Min(0, Health);
|
|
|
|
// activate death events
|
|
if( default.KismetDeathDelayTime > 0 )
|
|
{
|
|
DelayTriggerDeath();
|
|
}
|
|
else
|
|
{
|
|
TriggerEventClass( class'SeqEvent_Death', self );
|
|
}
|
|
|
|
KismetDeathDelayTime = default.KismetDeathDelayTime + WorldInfo.TimeSeconds;
|
|
|
|
// and abort any latent actions
|
|
foreach LatentActions(Action)
|
|
{
|
|
Action.AbortFor(self);
|
|
}
|
|
LatentActions.Length = 0;
|
|
// notify the vehicle we are currently driving
|
|
if ( DrivenVehicle != None )
|
|
{
|
|
Velocity = DrivenVehicle.Velocity;
|
|
DrivenVehicle.DriverDied(DamageType);
|
|
}
|
|
else if ( Weapon != None )
|
|
{
|
|
Weapon.HolderDied();
|
|
ThrowWeaponOnDeath();
|
|
}
|
|
// notify the gameinfo of the death
|
|
if ( Controller != None )
|
|
{
|
|
WorldInfo.Game.Killed(Killer, Controller, self, damageType);
|
|
}
|
|
else
|
|
{
|
|
WorldInfo.Game.Killed(Killer, Controller(Owner), self, damageType);
|
|
}
|
|
DrivenVehicle = None;
|
|
// notify inventory manager
|
|
if ( InvManager != None )
|
|
{
|
|
InvManager.OwnerDied();
|
|
}
|
|
`if(`__TW_)
|
|
// push the corpse upward (@fixme - somebody please remove this?)
|
|
// Consider yourself removed - Ramm
|
|
//Velocity.Z *= 1.3;
|
|
`else
|
|
// push the corpse upward (@fixme - somebody please remove this?)
|
|
Velocity.Z *= 1.3;
|
|
`endif
|
|
|
|
// if this is a human player then force a replication update
|
|
if ( IsHumanControlled() )
|
|
{
|
|
PlayerController(Controller).ForceDeathUpdate();
|
|
}
|
|
NetUpdateFrequency = Default.NetUpdateFrequency;
|
|
PlayDying(DamageType, HitLocation);
|
|
return TRUE;
|
|
}
|
|
|
|
function DelayTriggerDeath()
|
|
{
|
|
TriggerEventClass( class'SeqEvent_Death', self );
|
|
}
|
|
|
|
event Falling();
|
|
|
|
event Landed(vector HitNormal, Actor FloorActor)
|
|
{
|
|
TakeFallingDamage();
|
|
if ( Health > 0 )
|
|
PlayLanded(Velocity.Z);
|
|
LastHitBy = None;
|
|
}
|
|
|
|
/**
|
|
* Called if bScriptTickSpecial is set
|
|
* This tick function is called after physics has executed
|
|
*/
|
|
event TickSpecial( float DeltaTime );
|
|
|
|
event HeadVolumeChange(PhysicsVolume newHeadVolume)
|
|
{
|
|
if ( (WorldInfo.NetMode == NM_Client) || (Controller == None) )
|
|
return;
|
|
if ( HeadVolume != None && 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;
|
|
}
|
|
|
|
event BreathTimer()
|
|
{
|
|
if ( HeadVolume.bWaterVolume )
|
|
{
|
|
if ( (Health < 0) || (WorldInfo.NetMode == NM_Client) || (DrivenVehicle != None) )
|
|
return;
|
|
TakeDrowningDamage();
|
|
if ( Health > 0 )
|
|
BreathTime = 2.0;
|
|
}
|
|
else
|
|
{
|
|
BreathTime = 0.0;
|
|
}
|
|
}
|
|
|
|
function TakeDrowningDamage();
|
|
|
|
function bool CheckWaterJump(out vector WallNormal)
|
|
{
|
|
local actor HitActor;
|
|
local vector HitLocation, HitNormal, Checkpoint, start, checkNorm, Extent;
|
|
|
|
if ( AIController(Controller) != None )
|
|
{
|
|
if ( Controller.InLatentExecution(Controller.LATENT_MOVETOWARD) && (Controller.Movetarget != None)
|
|
&& !Controller.MoveTarget.PhysicsVolume.bWaterVolume )
|
|
{
|
|
CheckPoint = Normal(Controller.MoveTarget.Location - Location);
|
|
}
|
|
else
|
|
{
|
|
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 * CylinderComponent.CollisionRadius * checkNorm;
|
|
Extent = CylinderComponent.CollisionRadius * vect(1,1,0);
|
|
Extent.Z = CylinderComponent.CollisionHeight;
|
|
HitActor = Trace(HitLocation, HitNormal, Checkpoint, Location, true, Extent,,TRACEFLAG_Blocking);
|
|
if ( (HitActor != None) && (Pawn(HitActor) == None) )
|
|
{
|
|
WallNormal = -1 * HitNormal;
|
|
start = Location;
|
|
start.Z += MaxOutOfWaterStepHeight;
|
|
checkPoint = start + 3.2 * CylinderComponent.CollisionRadius * WallNormal;
|
|
HitActor = Trace(HitLocation, HitNormal, Checkpoint, start, true,,,TRACEFLAG_Blocking);
|
|
if ( (HitActor == None) || (HitNormal.Z > 0.7) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//Player Jumped
|
|
function bool DoJump( bool bUpdating )
|
|
{
|
|
if (bJumpCapable && !bIsCrouched && !bWantsToCrouch && (Physics == PHYS_Walking || Physics == PHYS_Ladder || Physics == PHYS_Spider))
|
|
{
|
|
if ( Physics == PHYS_Spider )
|
|
`if(`__TW_PATHFINDING_)
|
|
Velocity = Velocity + (JumpZ * Floor);
|
|
`else
|
|
Velocity = JumpZ * Floor;
|
|
`endif
|
|
else if ( Physics == PHYS_Ladder )
|
|
Velocity.Z = 0;
|
|
else if ( bIsWalking )
|
|
Velocity.Z = Default.JumpZ;
|
|
else
|
|
Velocity.Z = JumpZ;
|
|
if (Base != None && !Base.bWorldGeometry && Base.Velocity.Z > 0.f)
|
|
{
|
|
Velocity.Z += Base.Velocity.Z;
|
|
}
|
|
SetPhysics(PHYS_Falling);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function PlayDyingSound();
|
|
|
|
function PlayHit(float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo)
|
|
{
|
|
if ( (Damage <= 0) && ((Controller == None) || !Controller.bGodMode) )
|
|
return;
|
|
|
|
LastPainTime = WorldInfo.TimeSeconds;
|
|
}
|
|
|
|
/** TurnOff()
|
|
Freeze pawn - stop sounds, animations, physics, weapon firing
|
|
*/
|
|
simulated function TurnOff()
|
|
{
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
RemoteRole = ROLE_SimulatedProxy;
|
|
}
|
|
if (WorldInfo.NetMode != NM_DedicatedServer && Mesh != None)
|
|
{
|
|
Mesh.bPauseAnims = true;
|
|
if (Physics == PHYS_RigidBody)
|
|
{
|
|
Mesh.PhysicsWeight = 1.0;
|
|
Mesh.bUpdateKinematicBonesFromAnimation = false;
|
|
}
|
|
}
|
|
SetCollision(true,false);
|
|
bNoWeaponFiring = true;
|
|
Velocity = vect(0,0,0);
|
|
SetPhysics(PHYS_None);
|
|
bIgnoreForces = true;
|
|
if (Weapon != None)
|
|
{
|
|
Weapon.StopFire(Weapon.CurrentFireMode);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Set physics for dying pawn
|
|
* Always set to falling, unless already a ragdoll
|
|
*/
|
|
function SetDyingPhysics()
|
|
{
|
|
if( Physics != PHYS_RigidBody )
|
|
{
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
}
|
|
|
|
State Dying
|
|
{
|
|
ignores Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer, FellOutOfWorld;
|
|
|
|
simulated function PlayWeaponSwitch(Weapon OldWeapon, Weapon NewWeapon) {}
|
|
simulated function PlayNextAnimation() {}
|
|
singular event BaseChange() {}
|
|
event Landed(vector HitNormal, Actor FloorActor) {}
|
|
|
|
function bool Died(Controller Killer, class<DamageType> damageType, vector HitLocation);
|
|
|
|
simulated singular event OutsideWorldBounds()
|
|
{
|
|
SetPhysics(PHYS_None);
|
|
SetHidden(True);
|
|
LifeSpan = FMin(LifeSpan, 1.0);
|
|
}
|
|
|
|
event Timer()
|
|
{
|
|
if ( !PlayerCanSeeMe() )
|
|
{
|
|
Destroy();
|
|
}
|
|
else
|
|
{
|
|
SetTimer(2.0, false);
|
|
}
|
|
}
|
|
|
|
event TakeDamage(int Damage, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
|
|
{
|
|
SetPhysics(PHYS_Falling);
|
|
|
|
if ( (Physics == PHYS_None) && (Momentum.Z < 0) )
|
|
Momentum.Z *= -1;
|
|
|
|
Velocity += 3 * momentum/(Mass + 200);
|
|
|
|
if ( damagetype == None )
|
|
{
|
|
// `warn("No damagetype for damage by "$instigatedby.pawn$" with weapon "$InstigatedBy.Pawn.Weapon);
|
|
DamageType = class'DamageType';
|
|
}
|
|
|
|
Health -= Damage;
|
|
}
|
|
|
|
event BeginState(Name PreviousStateName)
|
|
{
|
|
local Actor A;
|
|
local array<SequenceEvent> TouchEvents;
|
|
local int i;
|
|
|
|
if ( bTearOff && (WorldInfo.NetMode == NM_DedicatedServer) )
|
|
{
|
|
LifeSpan = 2.0;
|
|
}
|
|
else
|
|
{
|
|
SetTimer(5.0, false);
|
|
// add a failsafe termination
|
|
LifeSpan = 25.f;
|
|
}
|
|
|
|
SetDyingPhysics();
|
|
|
|
SetCollision(true, false);
|
|
|
|
if ( Controller != None )
|
|
{
|
|
if ( Controller.bIsPlayer )
|
|
{
|
|
DetachFromController();
|
|
}
|
|
else
|
|
{
|
|
`if(`__TW_)
|
|
DetachFromController(true);
|
|
`else
|
|
Controller.Destroy();
|
|
`endif
|
|
}
|
|
}
|
|
|
|
foreach TouchingActors(class'Actor', A)
|
|
{
|
|
if (A.FindEventsOfClass(class'SeqEvent_Touch', TouchEvents))
|
|
{
|
|
for (i = 0; i < TouchEvents.length; i++)
|
|
{
|
|
SeqEvent_Touch(TouchEvents[i]).NotifyTouchingPawnDied(self);
|
|
}
|
|
// clear array for next iteration
|
|
TouchEvents.length = 0;
|
|
}
|
|
}
|
|
foreach BasedActors(class'Actor', A)
|
|
{
|
|
A.PawnBaseDied();
|
|
}
|
|
}
|
|
|
|
Begin:
|
|
Sleep(0.2);
|
|
PlayDyingSound();
|
|
}
|
|
|
|
//=============================================================================
|
|
// Animation interface for controllers
|
|
|
|
/* PlayXXX() function called by controller to play transient animation actions
|
|
*/
|
|
/* PlayDying() is called on server/standalone game when killed
|
|
and also on net client when pawn gets bTearOff set to true (and bPlayedDeath is false)
|
|
*/
|
|
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
|
|
{
|
|
GotoState('Dying');
|
|
bReplicateMovement = false;
|
|
bTearOff = true;
|
|
Velocity += TearOffMomentum;
|
|
SetDyingPhysics();
|
|
bPlayedDeath = true;
|
|
|
|
KismetDeathDelayTime = default.KismetDeathDelayTime + WorldInfo.TimeSeconds;
|
|
}
|
|
|
|
simulated event TornOff()
|
|
{
|
|
// assume dead if bTearOff
|
|
if ( !bPlayedDeath )
|
|
{
|
|
PlayDying(HitDamageType,TakeHitLocation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PlayFootStepSound()
|
|
* called by AnimNotify_Footstep
|
|
*
|
|
* FootDown specifies which foot hit
|
|
*/
|
|
event PlayFootStepSound(int FootDown);
|
|
|
|
//=============================================================================
|
|
// Pawn internal animation functions
|
|
|
|
// Animation group checks (usually implemented in subclass)
|
|
function bool CannotJumpNow()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function PlayLanded(float impactVel);
|
|
|
|
native function Vehicle GetVehicleBase();
|
|
|
|
function Suicide()
|
|
{
|
|
KilledBy(self);
|
|
}
|
|
|
|
// toss out a weapon
|
|
// check before throwing
|
|
simulated function bool CanThrowWeapon()
|
|
{
|
|
return ( (Weapon != None) && Weapon.CanThrow() );
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Vehicle driving
|
|
***********************************************************************************/
|
|
|
|
|
|
/**
|
|
* StartDriving() and StopDriving() also called on clients
|
|
* on transitions of DrivenVehicle variable.
|
|
* Network: ALL
|
|
*/
|
|
simulated event StartDriving(Vehicle V)
|
|
{
|
|
StopFiring();
|
|
if ( Health <= 0 )
|
|
return;
|
|
|
|
DrivenVehicle = V;
|
|
bForceNetUpdate = TRUE;
|
|
|
|
// Move the driver into position, and attach to car.
|
|
ShouldCrouch(false);
|
|
bIgnoreForces = true;
|
|
bCanTeleport = false;
|
|
BreathTime = 0.0;
|
|
V.AttachDriver( Self );
|
|
}
|
|
|
|
/**
|
|
* StartDriving() and StopDriving() also called on clients
|
|
* on transitions of DrivenVehicle variable.
|
|
* Network: ALL
|
|
*/
|
|
simulated event StopDriving(Vehicle V)
|
|
{
|
|
if ( Mesh != None )
|
|
{
|
|
Mesh.SetCullDistance(Default.Mesh.CachedMaxDrawDistance);
|
|
Mesh.SetShadowParent(None);
|
|
}
|
|
bForceNetUpdate = TRUE;
|
|
if (V != None )
|
|
{
|
|
V.StopFiring();
|
|
}
|
|
|
|
if ( Physics == PHYS_RigidBody )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DrivenVehicle = None;
|
|
bIgnoreForces = false;
|
|
SetHardAttach(false);
|
|
bCanTeleport = true;
|
|
bCollideWorld = true;
|
|
|
|
if ( V != None )
|
|
{
|
|
V.DetachDriver( Self );
|
|
}
|
|
|
|
SetCollision(true, true);
|
|
|
|
if ( Role == ROLE_Authority )
|
|
{
|
|
if ( PhysicsVolume.bWaterVolume && (Health > 0) )
|
|
{
|
|
SetPhysics(PHYS_Swimming);
|
|
}
|
|
else
|
|
{
|
|
SetPhysics(PHYS_Falling);
|
|
}
|
|
SetBase(None);
|
|
SetHidden(False);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Inventory related functions
|
|
//
|
|
|
|
/* AddDefaultInventory:
|
|
Add Pawn default Inventory.
|
|
Called from GameInfo.AddDefaultInventory()
|
|
*/
|
|
function AddDefaultInventory();
|
|
|
|
/* epic ===============================================
|
|
* ::CreateInventory
|
|
*
|
|
* Create Inventory Item, adds it to the Pawn's Inventory
|
|
* And returns it for post processing.
|
|
*
|
|
* =====================================================
|
|
*/
|
|
event final Inventory CreateInventory( class<Inventory> NewInvClass, optional bool bDoNotActivate )
|
|
{
|
|
if ( InvManager != None )
|
|
return InvManager.CreateInventory( NewInvClass, bDoNotActivate );
|
|
|
|
return None;
|
|
}
|
|
|
|
/* FindInventoryType:
|
|
returns the inventory item of the requested class if it exists in this Pawn's inventory
|
|
*/
|
|
simulated final function Inventory FindInventoryType(class<Inventory> DesiredClass, optional bool bAllowSubclass)
|
|
{
|
|
return (InvManager != None) ? InvManager.FindInventoryType(DesiredClass, bAllowSubclass) : None;
|
|
}
|
|
|
|
/** Hook called from HUD actor. Gives access to HUD and Canvas */
|
|
simulated function DrawHUD( HUD H )
|
|
{
|
|
if ( InvManager != None )
|
|
{
|
|
InvManager.DrawHUD( H );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toss active weapon using default settings (location+velocity).
|
|
*
|
|
* @param DamageType allows this function to do different behaviors based on the damage type
|
|
*/
|
|
function ThrowActiveWeapon( optional bool bDestroyWeap )
|
|
{
|
|
if ( Weapon != None )
|
|
{
|
|
TossInventory(Weapon);
|
|
}
|
|
}
|
|
|
|
function TossInventory(Inventory Inv, optional vector ForceVelocity)
|
|
{
|
|
local vector POVLoc, TossVel;
|
|
local rotator POVRot;
|
|
local Vector X,Y,Z;
|
|
|
|
if ( ForceVelocity != vect(0,0,0) )
|
|
{
|
|
TossVel = ForceVelocity;
|
|
}
|
|
else
|
|
{
|
|
GetActorEyesViewPoint(POVLoc, POVRot);
|
|
TossVel = Vector(POVRot);
|
|
TossVel = TossVel * ((Velocity Dot TossVel) + 500) + Vect(0,0,200);
|
|
}
|
|
|
|
GetAxes(Rotation, X, Y, Z);
|
|
Inv.DropFrom(Location + 0.8 * CylinderComponent.CollisionRadius * X - 0.5 * CylinderComponent.CollisionRadius * Y, TossVel);
|
|
}
|
|
|
|
/* SetActiveWeapon
|
|
Set this weapon as the Pawn's active weapon
|
|
*/
|
|
simulated function SetActiveWeapon( Weapon NewWeapon )
|
|
{
|
|
if ( InvManager != None )
|
|
{
|
|
InvManager.SetCurrentWeapon( NewWeapon );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Player just changed weapon. Called from InventoryManager::ChangedWeapon().
|
|
* Network: Local Player and Server.
|
|
*
|
|
* @param OldWeapon Old weapon held by Pawn.
|
|
* @param NewWeapon New weapon held by Pawn.
|
|
*/
|
|
simulated function PlayWeaponSwitch(Weapon OldWeapon, Weapon NewWeapon);
|
|
|
|
// Cheats - invoked by CheatManager
|
|
function bool CheatWalk()
|
|
{
|
|
UnderWaterTime = Default.UnderWaterTime;
|
|
SetCollision(true, true);
|
|
SetPhysics(PHYS_Falling);
|
|
bCollideWorld = true;
|
|
SetPushesRigidBodies(Default.bPushesRigidBodies);
|
|
return true;
|
|
}
|
|
|
|
function bool CheatGhost()
|
|
{
|
|
UnderWaterTime = -1.0;
|
|
SetCollision(false, false);
|
|
bCollideWorld = false;
|
|
SetPushesRigidBodies(false);
|
|
return true;
|
|
}
|
|
|
|
function bool CheatFly()
|
|
{
|
|
UnderWaterTime = Default.UnderWaterTime;
|
|
SetCollision(true, true);
|
|
bCollideWorld = true;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the collision radius of our cylinder
|
|
* collision component.
|
|
*
|
|
* @return the collision radius of our pawn
|
|
*/
|
|
simulated function float GetCollisionRadius()
|
|
{
|
|
return (CylinderComponent != None) ? CylinderComponent.CollisionRadius : 0.f;
|
|
}
|
|
|
|
/**
|
|
* Returns the collision height of our cylinder
|
|
* collision component.
|
|
*
|
|
* @return collision height of our pawn
|
|
*/
|
|
simulated function float GetCollisionHeight()
|
|
{
|
|
return (CylinderComponent != None) ? CylinderComponent.CollisionHeight : 0.f;
|
|
}
|
|
|
|
/** @return a vector representing the box around this pawn's cylinder collision component, for use with traces */
|
|
simulated final function vector GetCollisionExtent()
|
|
{
|
|
local vector Extent;
|
|
|
|
Extent = GetCollisionRadius() * vect(1,1,0);
|
|
Extent.Z = GetCollisionHeight();
|
|
return Extent;
|
|
}
|
|
|
|
/**
|
|
* Pawns by nature are not stationary. Override if you want exact findings
|
|
*/
|
|
function bool IsStationary()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
event SpawnedByKismet()
|
|
{
|
|
// notify controller
|
|
if (Controller != None)
|
|
{
|
|
Controller.SpawnedByKismet();
|
|
}
|
|
}
|
|
|
|
|
|
/** Performs actual attachment. Can be subclassed for class specific behaviors. */
|
|
function DoKismetAttachment(Actor Attachment, SeqAct_AttachToActor Action)
|
|
{
|
|
local bool bOldCollideActors, bOldBlockActors, bValidBone, bValidSocket;
|
|
|
|
// If a bone/socket has been specified, see if it is valid
|
|
if( Mesh != None && Action.BoneName != '' )
|
|
{
|
|
// See if the bone name refers to an existing socket on the skeletal mesh.
|
|
bValidSocket = (Mesh.GetSocketByName(Action.BoneName) != None);
|
|
bValidBone = (Mesh.MatchRefBone(Action.BoneName) != INDEX_NONE);
|
|
|
|
// Issue a warning if we were expecting to attach to a bone/socket, but it could not be found.
|
|
if( !bValidBone && !bValidSocket )
|
|
{
|
|
`log(WorldInfo.TimeSeconds @ class @ GetFuncName() @ "bone or socket" @ Action.BoneName @ "not found on actor" @ Self @ "with mesh" @ Mesh);
|
|
}
|
|
}
|
|
|
|
// Special case for handling relative location/rotation w/ bone or socket
|
|
if( bValidBone || bValidSocket )
|
|
{
|
|
// disable collision, so we can successfully move the attachment
|
|
bOldCollideActors = Attachment.bCollideActors;
|
|
bOldBlockActors = Attachment.bBlockActors;
|
|
Attachment.SetCollision(FALSE, FALSE);
|
|
Attachment.SetHardAttach(Action.bHardAttach);
|
|
|
|
// Sockets by default move the actor to the socket location.
|
|
// This is not the case for bones!
|
|
// So if we use relative offsets, then first move attachment to bone's location.
|
|
if( bValidBone && !bValidSocket )
|
|
{
|
|
if( Action.bUseRelativeOffset )
|
|
{
|
|
Attachment.SetLocation(Mesh.GetBoneLocation(Action.BoneName));
|
|
}
|
|
|
|
if( Action.bUseRelativeRotation )
|
|
{
|
|
Attachment.SetRotation(QuatToRotator(Mesh.GetBoneQuaternion(Action.BoneName)));
|
|
}
|
|
}
|
|
|
|
// Attach attachment to base.
|
|
Attachment.SetBase(Self,, Mesh, Action.BoneName);
|
|
|
|
if( Action.bUseRelativeRotation )
|
|
{
|
|
Attachment.SetRelativeRotation(Attachment.RelativeRotation + Action.RelativeRotation);
|
|
}
|
|
|
|
// if we're using the offset, place attachment relatively to the target
|
|
if( Action.bUseRelativeOffset )
|
|
{
|
|
Attachment.SetRelativeLocation(Attachment.RelativeLocation + Action.RelativeOffset);
|
|
}
|
|
|
|
// restore previous collision
|
|
Attachment.SetCollision(bOldCollideActors, bOldBlockActors);
|
|
}
|
|
else
|
|
{
|
|
// otherwise base on location
|
|
Super.DoKismetAttachment(Attachment, Action);
|
|
}
|
|
}
|
|
|
|
|
|
/** returns the amount this pawn's damage should be scaled by */
|
|
function float GetDamageScaling()
|
|
{
|
|
return DamageScaling;
|
|
}
|
|
|
|
function OnSetMaterial(SeqAct_SetMaterial Action)
|
|
{
|
|
if (Mesh != None)
|
|
{
|
|
Mesh.SetMaterial( Action.MaterialIndex, Action.NewMaterial );
|
|
}
|
|
}
|
|
|
|
/** Kismet teleport handler, overridden so that updating rotation properly updates our Controller as well */
|
|
simulated function OnTeleport(SeqAct_Teleport Action)
|
|
{
|
|
local array<Object> objVars;
|
|
|
|
// find the first supplied actor
|
|
Action.GetObjectVars(objVars,"Destination");
|
|
if( !HandleTeleport( objVars, Action.bUpdateRotation, Action.bCheckOverlap, ,Action.TeleportVolumes ) )
|
|
{
|
|
`warn( "failed to handle teleport kismet action properly"@Action );
|
|
}
|
|
}
|
|
|
|
simulated function bool HandleTeleport( array<Object> DestList, bool bUpdateRotation, bool bCheckOverlap, optional float TeleportDistance, optional array<Volume> TeleportVolumes, optional int PreferredDestIndex )
|
|
{
|
|
local int idx, cnt;
|
|
local Actor destActor, tempActor, A;
|
|
local Controller C;
|
|
local bool bOccupiedDest, bColliding;
|
|
local Vector Extent;
|
|
|
|
Extent = GetCollisionExtent();
|
|
bOccupiedDest = FALSE;
|
|
|
|
// Make sure there's actually a list
|
|
if (DestList.Length > 0)
|
|
{
|
|
// Make sure INDEX_NONE (-1) gets resolved before accessing the DestList
|
|
idx = (PreferredDestIndex >= 0) ? PreferredDestIndex : 0;
|
|
if( idx >= DestList.length )
|
|
{
|
|
idx = 0;
|
|
PreferredDestIndex = 0;
|
|
}
|
|
cnt = 0;
|
|
|
|
do
|
|
{
|
|
tempActor = Actor(DestList[idx]);
|
|
if( tempActor != None )
|
|
{
|
|
// If its a player variable, teleport to the Pawn not the Controller.
|
|
C = Controller(tempActor);
|
|
if(C != None && C.Pawn != None)
|
|
{
|
|
tempActor = C.Pawn;
|
|
}
|
|
|
|
if( bCheckOverlap )
|
|
{
|
|
bColliding = FALSE;
|
|
foreach VisibleCollidingActors ( class'Actor', A, Extent.X * 2.f, tempActor.Location, FALSE, Extent, TRUE )
|
|
{
|
|
if( IsBlockedBy( A ) )
|
|
{
|
|
bColliding = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bOccupiedDest = bColliding;
|
|
}
|
|
|
|
destActor = tempActor;
|
|
if( (!bCheckOverlap || !bOccupiedDest) && destActor != None )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Increment idx
|
|
++idx;
|
|
if( idx >= DestList.length )
|
|
{
|
|
idx = 0;
|
|
}
|
|
++cnt;
|
|
|
|
} until (idx == PreferredDestIndex || cnt >= DestList.Length);
|
|
}
|
|
else
|
|
{
|
|
`warn("Unable to teleport - no destination list given");
|
|
}
|
|
|
|
// and set to that actor's location
|
|
if( destActor != None && class'SeqAct_Teleport'.static.ShouldTeleport( self, destActor.Location, TeleportDistance, TeleportVolumes ) )
|
|
{
|
|
if (SetLocation(destActor.Location))
|
|
{
|
|
// If the pawn being teleported is a client player we need to tell him to move on his side
|
|
if (!IsLocallyControlled() && PlayerController(Controller) != None)
|
|
{
|
|
PlayerController(Controller).ClientSetLocation(destActor.Location, Rotation);
|
|
}
|
|
|
|
PlayTeleportEffect(false, true);
|
|
if( bUpdateRotation )
|
|
{
|
|
SetRotation(destActor.Rotation);
|
|
if (Controller != None)
|
|
{
|
|
Controller.SetRotation(destActor.Rotation);
|
|
Controller.ClientSetRotation(destActor.Rotation);
|
|
}
|
|
}
|
|
// Tell controller we teleported (Pass None to avoid recursion)
|
|
if( Controller != None )
|
|
{
|
|
Controller.OnTeleport( None );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
`warn("Unable to teleport to"@destActor);
|
|
return FALSE;
|
|
}
|
|
|
|
if( destActor == None )
|
|
{
|
|
`warn("Unable to teleport - no destination given");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* For debugging. Causes a string to be displayed on the HUD.
|
|
*/
|
|
final event MessagePlayer( coerce String Msg )
|
|
{
|
|
`if(`notdefined(FINAL_RELEASE))
|
|
local PlayerController PC;
|
|
|
|
foreach LocalPlayerControllers(class'PlayerController', PC)
|
|
{
|
|
PC.ClientMessage( Msg );
|
|
}
|
|
`endif
|
|
}
|
|
|
|
simulated event BecomeViewTarget(PlayerController PC)
|
|
{
|
|
if (PhysicsVolume != None)
|
|
{
|
|
PhysicsVolume.NotifyPawnBecameViewTarget(self, PC);
|
|
}
|
|
|
|
// if we don't normally replicate health, but will want to do so now to this client, force an update
|
|
if (!bReplicateHealthToAll && WorldInfo.NetMode != NM_Client)
|
|
{
|
|
PC.ForceSingleNetUpdateFor(self);
|
|
}
|
|
}
|
|
|
|
/** For AI debugging */
|
|
event SoakPause()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
ForEach WorldInfo.LocalPlayerControllers(class'PlayerController', PC)
|
|
{
|
|
PC.SoakPause(self);
|
|
break;
|
|
}
|
|
}
|
|
|
|
native function ClearConstraints();
|
|
native function AddPathConstraint( PathConstraint Constraint );
|
|
native function AddGoalEvaluator( PathGoalEvaluator Evaluator );
|
|
|
|
/**
|
|
* Path shaping creation functions...
|
|
* these functions by default will just new the class, but this offers a handy
|
|
* interface to override for to do things like pool the constraints
|
|
*/
|
|
function PathConstraint CreatePathConstraint( class<PathConstraint> ConstraintClass )
|
|
{
|
|
return new(self) ConstraintClass;
|
|
}
|
|
function PathGoalEvaluator CreatePathGoalEvaluator( class<PathGoalEvaluator> GoalEvalClass )
|
|
{
|
|
return new(self) GoalEvalClass;
|
|
}
|
|
|
|
native function IncrementPathStep( int Cnt, Canvas C );
|
|
native function IncrementPathChild( int Cnt, Canvas C );
|
|
native function DrawPathStep( Canvas C );
|
|
native function ClearPathStep();
|
|
|
|
simulated function ZeroMovementVariables()
|
|
{
|
|
Velocity = vect(0,0,0);
|
|
Acceleration = vect(0,0,0);
|
|
}
|
|
|
|
simulated function SetCinematicMode( bool bInCinematicMode );
|
|
|
|
native function SetRootMotionInterpCurrentTime( float inTime, optional float DeltaTime, optional bool bUpdateSkelPose );
|
|
|
|
/** Set a ScalarParameter to Interpolate */
|
|
final simulated native function SetScalarParameterInterp(const out ScalarParameterInterpStruct ScalarParameterInterp);
|
|
|
|
/** Simple interface for handling pawn dialogue. */
|
|
simulated event Speak(SoundCue Cue)
|
|
{
|
|
// Trivial implementation for now.
|
|
|
|
// @TODO: handle things like
|
|
// - speaking one line at a time
|
|
// - playing facefx if appropriate
|
|
// - dialogue-specific soundmodes
|
|
// - etc
|
|
|
|
PlaySound(Cue, TRUE);
|
|
}
|
|
|
|
/**
|
|
* Handler for the SeqAct_SetVelocity action. Allows level designer to impart a velocity on the actor.
|
|
*/
|
|
simulated function OnSetVelocity( SeqAct_SetVelocity Action )
|
|
{
|
|
Super.OnSetVelocity(Action);
|
|
// fake acceleration - Assuming default delta time is 0.2f
|
|
if (Action.VelocityMag == 0)
|
|
{
|
|
Acceleration = vect(0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
Acceleration = Velocity/0.2f;
|
|
}
|
|
}
|
|
|
|
`if(`__TW_LIGHTING_MODIFICATIONS_) // Custom lighting channel implementation
|
|
/** Set the lighting channels on all the appropriate pawn meshes */
|
|
simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingChannels)
|
|
{
|
|
}
|
|
`endif
|
|
|
|
defaultproperties
|
|
{
|
|
Begin Object Class=SpriteComponent Name=Sprite
|
|
Sprite=Texture2D'EditorResources.S_Actor'
|
|
HiddenGame=True
|
|
AlwaysLoadOnClient=False
|
|
AlwaysLoadOnServer=False
|
|
SpriteCategoryName="Pawns"
|
|
End Object
|
|
Components.Add(Sprite)
|
|
|
|
// Pawns often manipulate physics components so need to be done pre-async
|
|
TickGroup=TG_PreAsyncWork
|
|
|
|
InventoryManagerClass=class'InventoryManager'
|
|
ControllerClass=class'AIController'
|
|
|
|
// Flags
|
|
bCanBeDamaged=true
|
|
bCanCrouch=false
|
|
bCanFly=false
|
|
bCanJump=true
|
|
bCanSwim=false
|
|
bCanTeleport=true
|
|
bCanWalk=true
|
|
bJumpCapable=true
|
|
bProjTarget=true
|
|
bSimulateGravity=true
|
|
bShouldBaseAtStartup=true
|
|
|
|
// Locomotion
|
|
WalkingPhysics=PHYS_Walking
|
|
LandMovementState=PlayerWalking
|
|
WaterMovementState=PlayerSwimming
|
|
|
|
AccelRate=+02048.000000
|
|
DesiredSpeed=+00001.000000
|
|
MaxDesiredSpeed=+00001.000000
|
|
MaxFallSpeed=+1200.0
|
|
AIMaxFallSpeedFactor=1.0
|
|
NonPreferredVehiclePathMultiplier=1.0
|
|
|
|
AirSpeed=+00600.000000
|
|
GroundSpeed=+00600.000000
|
|
JumpZ=+00420.000000
|
|
OutofWaterZ=+420.0
|
|
LadderSpeed=+200.0
|
|
WaterSpeed=+00300.000000
|
|
|
|
bLimitFallAccel=TRUE
|
|
AirControl=+0.05
|
|
|
|
CrouchedPct=+0.5
|
|
WalkingPct=+0.5
|
|
MovementSpeedModifier=+1.0
|
|
|
|
// Sound
|
|
bLOSHearing=true
|
|
HearingThreshold=+2800.0
|
|
SoundDampening=+00001.000000
|
|
noise1time=-00010.000000
|
|
noise2time=-00010.000000
|
|
|
|
// Physics
|
|
AvgPhysicsTime=+00000.100000
|
|
bPushesRigidBodies=false
|
|
RBPushRadius=10.0
|
|
RBPushStrength=50.0
|
|
|
|
// FOV / Sight
|
|
ViewPitchMin=-16384
|
|
ViewPitchMax=16383
|
|
RotationRate=(Pitch=20000,Yaw=20000,Roll=20000)
|
|
MaxPitchLimit=3072
|
|
|
|
SightRadius=+05000.000000
|
|
|
|
// Network
|
|
RemoteRole=ROLE_SimulatedProxy
|
|
NetPriority=+00002.000000
|
|
bUpdateSimulatedPosition=true
|
|
|
|
// GamePlay
|
|
DamageScaling=+00001.000000
|
|
Health=100
|
|
bReplicateHealthToAll=false
|
|
|
|
// Collision
|
|
BaseEyeHeight=+00064.000000
|
|
EyeHeight=+00054.000000
|
|
|
|
CrouchHeight=+40.0
|
|
CrouchRadius=+34.0
|
|
|
|
MaxStepHeight=35.0
|
|
MaxJumpHeight=96.0
|
|
WalkableFloorZ=0.7 // 0.7 ~= 45 degree angle for floor
|
|
LedgeCheckThreshold=4.0f
|
|
|
|
MaxOutOfWaterStepHeight=40.0
|
|
AllowedYawError=2000
|
|
Mass=+00100.000000
|
|
|
|
bCollideActors=true
|
|
bCollideWorld=true
|
|
bBlockActors=true
|
|
|
|
Begin Object Class=CylinderComponent Name=CollisionCylinder
|
|
CollisionRadius=+0034.000000
|
|
CollisionHeight=+0078.000000
|
|
BlockNonZeroExtent=true
|
|
BlockZeroExtent=true
|
|
BlockActors=true
|
|
CollideActors=true
|
|
End Object
|
|
CollisionComponent=CollisionCylinder
|
|
CylinderComponent=CollisionCylinder
|
|
Components.Add(CollisionCylinder)
|
|
|
|
Begin Object Class=ArrowComponent Name=Arrow
|
|
ArrowColor=(R=150,G=200,B=255)
|
|
bTreatAsASprite=True
|
|
SpriteCategoryName="Pawns"
|
|
End Object
|
|
Components.Add(Arrow)
|
|
|
|
VehicleCheckRadius=150
|
|
|
|
bAllowLedgeOverhang=TRUE
|
|
|
|
RootMotionInterpRate=1.f
|
|
|
|
bModifyNavPointDest=true
|
|
}
|