Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
ライブエディター
未変更行を折りたたむ
折り返しなし
レイアウト
分割
統合
比較精度
スマート
単語
文字
シンタックスハイライト
構文を選択
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
Untitled diff
作成日
9 年前
差分は期限切れになりません
クリア
エクスポート
共有
説明
0 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
799 行
すべてコピー
0 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
799 行
すべてコピー
//=============================================================================
//=============================================================================
// ScriptedPawn.
// ScriptedPawn.
//=============================================================================
//=============================================================================
class ScriptedPawn expands Pawn
class ScriptedPawn expands Pawn
abstract
abstract
native;
native;
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// Enumerations
// Enumerations
enum EDestinationType {
enum EDestinationType {
DEST_Failure,
DEST_Failure,
DEST_NewLocation,
DEST_NewLocation,
DEST_SameLocation
DEST_SameLocation
};
};
enum EAllianceType {
enum EAllianceType {
ALLIANCE_Friendly,
ALLIANCE_Friendly,
ALLIANCE_Neutral,
ALLIANCE_Neutral,
ALLIANCE_Hostile
ALLIANCE_Hostile
};
};
enum ERaiseAlarmType {
enum ERaiseAlarmType {
RAISEALARM_BeforeAttacking,
RAISEALARM_BeforeAttacking,
RAISEALARM_BeforeFleeing,
RAISEALARM_BeforeFleeing,
RAISEALARM_Never
RAISEALARM_Never
};
};
enum ESeekType {
enum ESeekType {
SEEKTYPE_None,
SEEKTYPE_None,
SEEKTYPE_Sound,
SEEKTYPE_Sound,
SEEKTYPE_Sight,
SEEKTYPE_Sight,
SEEKTYPE_Guess,
SEEKTYPE_Guess,
SEEKTYPE_Carcass
SEEKTYPE_Carcass
};
};
enum EHitLocation {
enum EHitLocation {
HITLOC_None,
HITLOC_None,
HITLOC_HeadFront,
HITLOC_HeadFront,
HITLOC_HeadBack,
HITLOC_HeadBack,
HITLOC_TorsoFront,
HITLOC_TorsoFront,
HITLOC_TorsoBack,
HITLOC_TorsoBack,
HITLOC_LeftLegFront,
HITLOC_LeftLegFront,
HITLOC_LeftLegBack,
HITLOC_LeftLegBack,
HITLOC_RightLegFront,
HITLOC_RightLegFront,
HITLOC_RightLegBack,
HITLOC_RightLegBack,
HITLOC_LeftArmFront,
HITLOC_LeftArmFront,
HITLOC_LeftArmBack,
HITLOC_LeftArmBack,
HITLOC_RightArmFront,
HITLOC_RightArmFront,
HITLOC_RightArmBack
HITLOC_RightArmBack
};
};
enum ETurning {
enum ETurning {
TURNING_None,
TURNING_None,
TURNING_Left,
TURNING_Left,
TURNING_Right
TURNING_Right
};
};
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// Structures
// Structures
struct WanderCandidates {
struct WanderCandidates {
var WanderPoint point;
var WanderPoint point;
var Actor waypoint;
var Actor waypoint;
var float dist;
var float dist;
};
};
struct FleeCandidates {
struct FleeCandidates {
var HidePoint point;
var HidePoint point;
var Actor waypoint;
var Actor waypoint;
var Vector location;
var Vector location;
var float score;
var float score;
var float dist;
var float dist;
};
};
struct NearbyProjectile {
struct NearbyProjectile {
var DeusExProjectile projectile;
var DeusExProjectile projectile;
var vector location;
var vector location;
var float dist;
var float dist;
var float range;
var float range;
};
};
struct NearbyProjectileList {
struct NearbyProjectileList {
var NearbyProjectile list[8];
var NearbyProjectile list[8];
var vector center;
var vector center;
};
};
struct InitialAllianceInfo {
struct InitialAllianceInfo {
var() Name AllianceName;
var() Name AllianceName;
var() float AllianceLevel;
var() float AllianceLevel;
var() bool bPermanent;
var() bool bPermanent;
};
};
struct AllianceInfoEx {
struct AllianceInfoEx {
var Name AllianceName;
var Name AllianceName;
var float AllianceLevel;
var float AllianceLevel;
var float AgitationLevel;
var float AgitationLevel;
var bool bPermanent;
var bool bPermanent;
};
};
struct InventoryItem {
struct InventoryItem {
var() class<Inventory> Inventory;
var() class<Inventory> Inventory;
var() int Count;
var() int Count;
};
};
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// Variables
// Variables
var WanderPoint lastPoints[2];
var WanderPoint lastPoints[2];
var float Restlessness; // 0-1
var float Restlessness; // 0-1
var float Wanderlust; // 0-1
var float Wanderlust; // 0-1
var float Cowardice; // 0-1
var float Cowardice; // 0-1
var(Combat) float BaseAccuracy; // 0-1 or thereabouts
var(Combat) float BaseAccuracy; // 0-1 or thereabouts
var(Combat) float MaxRange;
var(Combat) float MaxRange;
var(Combat) float MinRange;
var(Combat) float MinRange;
var(Combat) float MinHealth;
var(Combat) float MinHealth;
var(AI) float RandomWandering; // 0-1
var(AI) float RandomWandering; // 0-1
var float sleepTime;
var float sleepTime;
var Actor destPoint;
var Actor destPoint;
var Vector destLoc;
var Vector destLoc;
var Vector useLoc;
var Vector useLoc;
var Rotator useRot;
var Rotator useRot;
var float seekDistance; // OBSOLETE
var float seekDistance; // OBSOLETE
var int SeekLevel;
var int SeekLevel;
var ESeekType SeekType;
var ESeekType SeekType;
var Pawn SeekPawn;
var Pawn SeekPawn;
var float CarcassTimer;
var float CarcassTimer;
var float CarcassHateTimer; // OBSOLETE
var float CarcassHateTimer; // OBSOLETE
var float CarcassCheckTimer;
var float CarcassCheckTimer;
var name PotentialEnemyAlliance;
var name PotentialEnemyAlliance;
var float PotentialEnemyTimer;
var float PotentialEnemyTimer;
var float BeamCheckTimer;
var float BeamCheckTimer;
var bool bSeekPostCombat;
var bool bSeekPostCombat;
var bool bSeekLocation;
var bool bSeekLocation;
var bool bInterruptSeek;
var bool bInterruptSeek;
var bool bAlliancesChanged; // set to True whenever someone changes AlliancesEx[i].AllianceLevel to indicate we must do alliance updating
var bool bAlliancesChanged; // set to True whenever someone changes AlliancesEx[i].AllianceLevel to indicate we must do alliance updating
var bool bNoNegativeAlliances; // True means we know all alliances are currently +, allows us to skip CheckEnemyPresence's slow part
var bool bNoNegativeAlliances; // True means we know all alliances are currently +, allows us to skip CheckEnemyPresence's slow part
var bool bSitAnywhere;
var bool bSitAnywhere;
var bool bSitInterpolation;
var bool bSitInterpolation;
var bool bStandInterpolation;
var bool bStandInterpolation;
var float remainingSitTime;
var float remainingSitTime;
var float remainingStandTime;
var float remainingStandTime;
var vector StandRate;
var vector StandRate;
var float ReloadTimer;
var float ReloadTimer;
var bool bReadyToReload;
var bool bReadyToReload;
var(Pawn) class<carcass> CarcassType; // mesh to use when killed from the front
var(Pawn) class<carcass> CarcassType; // mesh to use when killed from the front
// Advanced AI attributes.
// Advanced AI attributes.
var(Orders) name Orders; // orders a creature is carrying out
var(Orders) name Orders; // orders a creature is carrying out
// will be initial state, plus creature will attempt
// will be initial state, plus creature will attempt
// to return to this state
// to return to this state
var(Orders) name OrderTag; // tag of object referred to by orders
var(Orders) name OrderTag; // tag of object referred to by orders
var(Orders) name HomeTag; // tag of object to use as home base
var(Orders) name HomeTag; // tag of object to use as home base
var(Orders) float HomeExtent; // extent of home base
var(Orders) float HomeExtent; // extent of home base
var actor OrderActor; // object referred to by orders (if applicable)
var actor OrderActor; // object referred to by orders (if applicable)
var name NextAnim; // used in states with multiple, sequenced animations
var name NextAnim; // used in states with multiple, sequenced animations
var float WalkingSpeed; // 0-1
var float WalkingSpeed; // 0-1
var(Combat) float ProjectileSpeed;
var(Combat) float ProjectileSpeed;
var name LastPainAnim;
var name LastPainAnim;
var float LastPainTime;
var float LastPainTime;
var vector DesiredPrePivot;
var vector DesiredPrePivot;
var float PrePivotTime;
var float PrePivotTime;
var vector PrePivotOffset;
var vector PrePivotOffset;
var bool bCanBleed; // true if an NPC can bleed
var bool bCanBleed; // true if an NPC can bleed
var float BleedRate; // how profusely the NPC is bleeding; 0-1
var float BleedRate; // how profusely the NPC is bleeding; 0-1
var float DropCounter; // internal; used in tick()
var float DropCounter; // internal; used in tick()
var() float ClotPeriod; // seconds it takes bleedRate to go from 1 to 0
var() float ClotPeriod; // seconds it takes bleedRate to go from 1 to 0
var bool bAcceptBump; // ugly hack
var bool bAcceptBump; // ugly hack
var bool bCanFire; // true if pawn is capable of shooting asynchronously
var bool bCanFire; // true if pawn is capable of shooting asynchronously
var(AI) bool bKeepWeaponDrawn; // true if pawn should always keep weapon drawn
var(AI) bool bKeepWeaponDrawn; // true if pawn should always keep weapon drawn
var(AI) bool bShowPain; // true if pawn should play pain animations
var(AI) bool bShowPain; // true if pawn should play pain animations
var(AI) bool bCanSit; // true if pawn can sit
var(AI) bool bCanSit; // true if pawn can sit
var(AI) bool bAlwaysPatrol; // true if stasis should be disabled during patrols
var(AI) bool bAlwaysPatrol; // true if stasis should be disabled during patrols
var(AI) bool bPlayIdle; // true if pawn should fidget while he's standing
var(AI) bool bPlayIdle; // true if pawn should fidget while he's standing
var(AI) bool bLeaveAfterFleeing; // true if pawn should disappear after fleeing
var(AI) bool bLeaveAfterFleeing; // true if pawn should disappear after fleeing
var(AI) bool bLikesNeutral; // true if pawn should treat neutrals as friendlies
var(AI) bool bLikesNeutral; // true if pawn should treat neutrals as friendlies
var(AI) bool bUseFirstSeatOnly; // true if only the nearest chair should be used for
var(AI) bool bUseFirstSeatOnly; // true if only the nearest chair should be used for
var(AI) bool bCower; // true if fearful pawns should cower instead of fleeing
var(AI) bool bCower; // true if fearful pawns should cower instead of fleeing
var(AI) bool bMakeFemale; // true if pawn is female
var(AI) bool bMakeFemale; // true if pawn is female
var HomeBase HomeActor; // home base
var HomeBase HomeActor; // home base
var Vector HomeLoc; // location of home base
var Vector HomeLoc; // location of home base
var Vector HomeRot; // rotation of home base
var Vector HomeRot; // rotation of home base
var bool bUseHome; // true if home base should be used
var bool bUseHome; // true if home base should be used
var bool bInterruptState; // true if the state can be interrupted
var bool bInterruptState; // true if the state can be interrupted
var bool bCanConverse; // true if the pawn can converse
var bool bCanConverse; // true if the pawn can converse
var() bool bImportant; // true if this pawn is game-critical
var() bool bImportant; // true if this pawn is game-critical
var() bool bInvincible; // true if this pawn cannot be killed
var() bool bInvincible; // true if this pawn cannot be killed
var bool bInitialized; // true if this pawn has been initialized
var bool bInitialized; // true if this pawn has been initialized
var(Combat) bool bAvoidAim; // avoid enemy's line of fire
var(Combat) bool bAvoidAim; // avoid enemy's line of fire
var(Combat) bool bAimForHead; // aim for the enemy's head
var(Combat) bool bAimForHead; // aim for the enemy's head
var(Combat) bool bDefendHome; // defend the home base
var(Combat) bool bDefendHome; // defend the home base
var bool bCanCrouch; // whether we should crouch when firing
var bool bCanCrouch; // whether we should crouch when firing
var bool bSeekCover; // seek cover
var bool bSeekCover; // seek cover
var bool bSprint; // sprint in random directions
var bool bSprint; // sprint in random directions
var(Combat) bool bUseFallbackWeapons; // use fallback weapons even when others are available
var(Combat) bool bUseFallbackWeapons; // use fallback weapons even when others are available
var float AvoidAccuracy; // how well we avoid enemy's line of fire; 0-1
var float AvoidAccuracy; // how well we avoid enemy's line of fire; 0-1
var bool bAvoidHarm; // avoid painful projectiles, gas clouds, etc.
var bool bAvoidHarm; // avoid painful projectiles, gas clouds, etc.
var float HarmAccuracy; // how well we avoid harm; 0-1
var float HarmAccuracy; // how well we avoid harm; 0-1
var float CrouchRate; // how often the NPC crouches, if bCrouch enabled; 0-1
var float CrouchRate; // how often the NPC crouches, if bCrouch enabled; 0-1
var float SprintRate; // how often the NPC randomly sprints if bSprint enabled; 0-1
var float SprintRate; // how often the NPC randomly sprints if bSprint enabled; 0-1
var float CloseCombatMult; // multiplier for how often the NPC sprints in close combat; 0-1
var float CloseCombatMult; // multiplier for how often the NPC sprints in close combat; 0-1
// If a stimulation is enabled, it causes an NPC to hate the stimulator
// If a stimulation is enabled, it causes an NPC to hate the stimulator
//var(Stimuli) bool bHateFutz;
//var(Stimuli) bool bHateFutz;
var(Stimuli) bool bHateHacking; // new
var(Stimuli) bool bHateHacking; // new
var(Stimuli) bool bHateWeapon;
var(Stimuli) bool bHateWeapon;
var(Stimuli) bool bHateShot;
var(Stimuli) bool bHateShot;
var(Stimuli) bool bHateInjury;
var(Stimuli) bool bHateInjury;
var(Stimuli) bool bHateIndirectInjury; // new
var(Stimuli) bool bHateIndirectInjury; // new
//var(Stimuli) bool bHateGore;
//var(Stimuli) bool bHateGore;
var(Stimuli) bool bHateCarcass; // new
var(Stimuli) bool bHateCarcass; // new
var(Stimuli) bool bHateDistress;
var(Stimuli) bool bHateDistress;
//var(Stimuli) bool bHateProjectiles;
//var(Stimuli) bool bHateProjectiles;
// If a reaction is enabled, the NPC will react appropriately to a stimulation
// If a reaction is enabled, the NPC will react appropriately to a stimulation
var(Reactions) bool bReactFutz; // new
var(Reactions) bool bReactFutz; // new
var(Reactions) bool bReactPresence; // React to the presence of an enemy (attacking)
var(Reactions) bool bReactPresence; // React to the presence of an enemy (attacking)
var(Reactions) bool bReactLoudNoise; // Seek the source of a loud noise (seeking)
var(Reactions) bool bReactLoudNoise; // Seek the source of a loud noise (seeking)
var(Reactions) bool bReactAlarm; // Seek the source of an alarm (seeking)
var(Reactions) bool bReactAlarm; // Seek the source of an alarm (seeking)
var(Reactions) bool bReactShot; // React to a gunshot fired by an enemy (attacking)
var(Reactions) bool bReactShot; // React to a gunshot fired by an enemy (attacking)
//var(Reactions) bool bReactGore; // React to gore appropriately (seeking)
//var(Reactions) bool bReactGore; // React to gore appropriately (seeking)
var(Reactions) bool bReactCarcass; // React to gore appropriately (seeking)
var(Reactions) bool bReactCarcass; // React to gore appropriately (seeking)
var(Reactions) bool bReactDistress; // React to distress appropriately (attacking)
var(Reactions) bool bReactDistress; // React to distress appropriately (attacking)
var(Reactions) bool bReactProjectiles; // React to harmful projectiles appropriately
var(Reactions) bool bReactProjectiles; // React to harmful projectiles appropriately
// If a fear is enabled, the NPC will run away from the stimulator
// If a fear is enabled, the NPC will run away from the stimulator
var(Fears) bool bFearHacking; // Run away from a hacker
var(Fears) bool bFearHacking; // Run away from a hacker
var(Fears) bool bFearWeapon; // Run away from a person holding a weapon
var(Fears) bool bFearWeapon; // Run away from a person holding a weapon
var(Fears) bool bFearShot; // Run away from a person who fires a shot
var(Fears) bool bFearShot; // Run away from a person who fires a shot
var(Fears) bool bFearInjury; // Run away from a person who causes injury
var(Fears) bool bFearInjury; // Run away from a person who causes injury
var(Fears) bool bFearIndirectInjury; // Run away from a person who causes indirect injury
var(Fears) bool bFearIndirectInjury; // Run away from a person who causes indirect injury
var(Fears) bool bFearCarcass; // Run away from a carcass
var(Fears) bool bFearCarcass; // Run away from a carcass
var(Fears) bool bFearDistress; // Run away from a person causing distress
var(Fears) bool bFearDistress; // Run away from a person causing distress
var(Fears) bool bFearAlarm; // Run away from the source of an alarm
var(Fears) bool bFearAlarm; // Run away from the source of an alarm
var(Fears) bool bFearProjectiles; // Run away from a projectile
var(Fears) bool bFearProjectiles; // Run away from a projectile
var(AI) bool bEmitDistress; // TRUE if NPC should emit distress
var(AI) bool bEmitDistress; // TRUE if NPC should emit distress
var(AI) ERaiseAlarmType RaiseAlarm; // When to raise an alarm
var(AI) ERaiseAlarmType RaiseAlarm; // When to raise an alarm
var bool bLookingForEnemy; // TRUE if we're actually looking for enemies
var bool bLookingForEnemy; // TRUE if we're actually looking for enemies
var bool bLookingForLoudNoise; // TRUE if we're listening for loud noises
var bool bLookingForLoudNoise; // TRUE if we're listening for loud noises
var bool bLookingForAlarm; // TRUE if we're listening for alarms
var bool bLookingForAlarm; // TRUE if we're listening for alarms
var bool bLookingForDistress; // TRUE if we're looking for signs of distress
var bool bLookingForDistress; // TRUE if we're looking for signs of distress
var bool bLookingForProjectiles; // TRUE if we're looking for projectiles that can harm us
var bool bLookingForProjectiles; // TRUE if we're looking for projectiles that can harm us
var bool bLookingForFutz; // TRUE if we're looking for people futzing with stuff
var bool bLookingForFutz; // TRUE if we're looking for people futzing with stuff
var bool bLookingForHacking; // TRUE if we're looking for people hacking stuff
var bool bLookingForHacking; // TRUE if we're looking for people hacking stuff
var bool bLookingForShot; // TRUE if we're listening for gunshots
var bool bLookingForShot; // TRUE if we're listening for gunshots
var bool bLookingForWeapon; // TRUE if we're looking for drawn weapons
var bool bLookingForWeapon; // TRUE if we're looking for drawn weapons
var bool bLookingForCarcass; // TRUE if we're looking for carcass events
var bool bLookingForCarcass; // TRUE if we're looking for carcass events
var bool bLookingForInjury; // TRUE if we're looking for injury events
var bool bLookingForInjury; // TRUE if we're looking for injury events
var bool bLookingForIndirectInjury; // TRUE if we're looking for secondary injury events
var bool bLookingForIndirectInjury; // TRUE if we're looking for secondary injury events
var bool bFacingTarget; // True if pawn is facing its target
var bool bFacingTarget; // True if pawn is facing its target
var(Combat) bool bMustFaceTarget; // True if an NPC must face his target to fire
var(Combat) bool bMustFaceTarget; // True if an NPC must face his target to fire
var(Combat) float FireAngle; // TOTAL angle (in degrees) in which a pawn may fire if bMustFaceTarget is false
var(Combat) float FireAngle; // TOTAL angle (in degrees) in which a pawn may fire if bMustFaceTarget is false
var(Combat) float FireElevation; // Max elevation distance required to attack (0=elevation doesn't matter)
var(Combat) float FireElevation; // Max elevation distance required to attack (0=elevation doesn't matter)
var(AI) int MaxProvocations;
var(AI) int MaxProvocations;
var float AgitationSustainTime;
var float AgitationSustainTime;
var float AgitationDecayRate;
var float AgitationDecayRate;
var float AgitationTimer;
var float AgitationTimer;
var float AgitationCheckTimer;
var float AgitationCheckTimer;
var float PlayerAgitationTimer; // hack
var float PlayerAgitationTimer; // hack
var float FearSustainTime;
var float FearSustainTime;
var float FearDecayRate;
var float FearDecayRate;
var float FearTimer;
var float FearTimer;
var float FearLevel;
var float FearLevel;
var float EnemyReadiness;
var float EnemyReadiness;
var float ReactionLevel;
var float ReactionLevel;
var float SurprisePeriod;
var float SurprisePeriod;
var float SightPercentage;
var float SightPercentage;
var float CycleTimer;
var float CycleTimer;
var float CyclePeriod;
var float CyclePeriod;
var float CycleCumulative;
var float CycleCumulative;
var Pawn CycleCandidate;
var Pawn CycleCandidate;
var float CycleDistance;
var float CycleDistance;
var AlarmUnit AlarmActor;
var AlarmUnit AlarmActor;
var float AlarmTimer;
var float AlarmTimer;
var float WeaponTimer;
var float WeaponTimer;
var float FireTimer;
var float FireTimer;
var float SpecialTimer;
var float SpecialTimer;
var float CrouchTimer;
var float CrouchTimer;
var float BackpedalTimer;
var float BackpedalTimer;
var bool bHasShadow;
var bool bHasShadow;
var float ShadowScale;
var float ShadowScale;
var bool bDisappear;
var bool bDisappear;
var bool bInTransientState; // true if the NPC is in a 3rd-tier (transient) state, like TakeHit
var bool bInTransientState; // true if the NPC is in a 3rd-tier (transient) state, like TakeHit
var(Alliances) InitialAllianceInfo InitialAlliances[8];
var(Alliances) InitialAllianceInfo InitialAlliances[8];
var AllianceInfoEx AlliancesEx[16];
var AllianceInfoEx AlliancesEx[16];
var bool bReverseAlliances;
var bool bReverseAlliances;
var(Pawn) float BaseAssHeight;
var(Pawn) float BaseAssHeight;
var(AI) float EnemyTimeout;
var(AI) float EnemyTimeout;
var float CheckPeriod;
var float CheckPeriod;
var float EnemyLastSeen;
var float EnemyLastSeen;
var int SeatSlot;
var int SeatSlot;
var Seat SeatActor;
var Seat SeatActor;
var int CycleIndex;
var int CycleIndex;
var int BodyIndex;
var int BodyIndex;
var bool bRunningStealthy;
var bool bRunningStealthy;
var bool bPausing;
var bool bPausing;
var bool bStaring;
var bool bStaring;
var bool bAttacking;
var bool bAttacking;
var bool bDistressed;
var bool bDistressed;
var bool bStunned;
var bool bStunned;
var bool bSitting;
var bool bSitting;
var bool bDancing;
var bool bDancing;
var bool bCrouching;
var bool bCrouching;
var bool bCanTurnHead;
var bool bCanTurnHead;
var(AI) bool bTickVisibleOnly; // Temporary?
var(AI) bool bTickVisibleOnly; // Temporary?
var() bool bInWorld;
var() bool bInWorld;
var vector WorldPosition;
var vector WorldPosition;
var bool bWorldCollideActors;
var bool bWorldCollideActors;
var bool bWorldBlockActors;
var bool bWorldBlockActors;
var bool bWorldBlockPlayers;
var bool bWorldBlockPlayers;
var() bool bHighlight; // should this object not highlight when focused?
var() bool bHighlight; // should this object not highlight when focused?
var(AI) bool bHokeyPokey;
var(AI) bool bHokeyPokey;
var bool bConvEndState;
var bool bConvEndState;
var(Inventory) InventoryItem InitialInventory[8]; // Initial inventory items carried by the pawn
var(Inventory) InventoryItem InitialInventory[8]; // Initial inventory items carried by the pawn
var Bool bConversationEndedNormally;
var Bool bConversationEndedNormally;
var Bool bInConversation;
var Bool bInConversation;
var Actor ConversationActor; // Actor currently speaking to or speaking to us
var Actor ConversationActor; // Actor currently speaking to or speaking to us
var() sound WalkSound;
var() sound WalkSound;
var float swimBubbleTimer;
var float swimBubbleTimer;
var bool bSpawnBubbles;
var bool bSpawnBubbles;
var bool bUseSecondaryAttack;
var bool bUseSecondaryAttack;
var bool bWalkAround;
var bool bWalkAround;
var bool bClearedObstacle;
var bool bClearedObstacle;
var bool bEnableCheckDest;
var bool bEnableCheckDest;
var ETurning TurnDirection;
var ETurning TurnDirection;
var ETurning NextDirection;
var ETurning NextDirection;
var Actor ActorAvoiding;
var Actor ActorAvoiding;
var float AvoidWallTimer;
var float AvoidWallTimer;
var float AvoidBumpTimer;
var float AvoidBumpTimer;
var float ObstacleTimer;
var float ObstacleTimer;
var vector LastDestLoc;
var vector LastDestLoc;
var vector LastDestPoint;
var vector LastDestPoint;
var int DestAttempts;
var int DestAttempts;
var float DeathTimer;
var float DeathTimer;
var float EnemyTimer;
var float EnemyTimer;
var float TakeHitTimer;
var float TakeHitTimer;
var name ConvOrders;
var name ConvOrders;
var name ConvOrderTag;
var name ConvOrderTag;
var float BurnPeriod;
var float BurnPeriod;
var float FutzTimer;
var float FutzTimer;
var float DistressTimer;
var float DistressTimer;
var vector SeatLocation;
var vector SeatLocation;
var Seat SeatHack;
var Seat SeatHack;
var bool bSeatLocationValid;
var bool bSeatLocationValid;
var bool bSeatHackUsed;
var bool bSeatHackUsed;
var bool bBurnedToDeath;
var bool bBurnedToDeath;
var bool bHasCloak;
var bool bHasCloak;
var bool bCloakOn;
var bool bCloakOn;
var int CloakThreshold;
var int CloakThreshold;
var float CloakEMPTimer;
var float CloakEMPTimer;
var float poisonTimer; // time remaining before next poison TakeDamage
var float poisonTimer; // time remaining before next poison TakeDamage
var int poisonCounter; // number of poison TakeDamages remaining
var int poisonCounter; // number of poison TakeDamages remaining
var int poisonDamage; // damage taken from poison effect
var int poisonDamage; // damage taken from poison effect
var Pawn Poisoner; // person who initiated PoisonEffect damage
var Pawn Poisoner; // person who initiated PoisonEffect damage
var Name Carcasses[4]; // list of carcasses seen
var Name Carcasses[4]; // list of carcasses seen
var int NumCarcasses; // number of carcasses seen
var int NumCarcasses; // number of carcasses seen
var float walkAnimMult;
var float walkAnimMult;
var float runAnimMult;
var float runAnimMult;
var bool bUsingHDTP;
var bool bUsingHDTP;
var string HDTPMeshName;
var string HDTPMeshName;
var string HDTPMeshTex[8];
var string HDTPMeshTex[8];
//GMDX
//GMDX
var bool bBurnedUp; //CyberP: for plasma rifle gibbing
var bool bBurnedUp; //CyberP: for plasma rifle gibbing
var bool bFlyer; //CyberP: for pawn knockback
var bool bFlyer; //CyberP: for pawn knockback
var bool bSetupPop; //CyberP: for headpop
var bool bSetupPop; //CyberP: for headpop
var int extraMult; //CyberP: variable headshot multiplier
var int extraMult; //CyberP: variable headshot multiplier
var bool bRadialBlastClamp; //CyberP: MiB radial attack
var bool bRadialBlastClamp; //CyberP: MiB radial attack
var bool bCanFlare; //CyberP: AI can throw flares if conditions are met
var bool bCanFlare; //CyberP: AI can throw flares if conditions are met
var bool bHasThrownFlare; //CyberP: has this NPC already thrown a flare?
var bool bHasThrownFlare; //CyberP: has this NPC already thrown a flare?
var bool bCanNade; //CyberP: AI can throw nades if conditions are met
var bool bCanNade; //CyberP: AI can throw nades if conditions are met
var bool bGrenadier; //CyberP: this pawn class can throw nades
var bool bGrenadier; //CyberP: this pawn class can throw nades
var bool bCommandoMelee;
var bool bCommandoMelee;
var bool bDefensiveStyle; //CyberP: this pawn doesn't always bum rush the player in close proximity
var bool bDefensiveStyle; //CyberP: this pawn doesn't always bum rush the player in close proximity
//var bool bFrontPop; //CyberP: for headpop
//var bool bFrontPop; //CyberP: for headpop
var int disturbanceCount; //CyberP: obsolete.
var int disturbanceCount; //CyberP: obsolete.
var bool bAlarmStatIncrease; //CyberP: AI get a one-time stat boost if alarm is triggered
var bool bAlarmStatIncrease; //CyberP: AI get a one-time stat boost if alarm is triggered
var bool bHasHelmet;
var bool bHasHelmet;
var bool bBiteClamp;
var bool bBiteClamp;
var bool bGreaselShould;
var bool bGreaselShould;
var(Filter) bool bHardcoreOnly; // CyberP: remove this pawn from world if we are not hardcore.
var(Filter) bool bHardcoreOnly; // CyberP: remove this pawn from world if we are not hardcore.
var(AI) bool bCanPop; //CyberP: if we can pop at all.
var(AI) bool bCanPop; //CyberP: if we can pop at all.
native(2102) final function ConBindEvents();
native(2102) final function ConBindEvents();
native(2105) final function bool IsValidEnemy(Pawn TestEnemy, optional bool bCheckAlliance);
native(2105) final function bool IsValidEnemy(Pawn TestEnemy, optional bool bCheckAlliance);
native(2106) final function EAllianceType GetAllianceType(Name AllianceName);
native(2106) final function EAllianceType GetAllianceType(Name AllianceName);
native(2107) final function EAllianceType GetPawnAllianceType(Pawn QueryPawn);
native(2107) final function EAllianceType GetPawnAllianceType(Pawn QueryPawn);
native(2108) final function bool HaveSeenCarcass(Name CarcassName);
native(2108) final function bool HaveSeenCarcass(Name CarcassName);
native(2109) final function AddCarcass(Name CarcassName);
native(2109) final function AddCarcass(Name CarcassName);
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// PreBeginPlay()
// PreBeginPlay()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function PreBeginPlay()
function PreBeginPlay()
{
{
local float saveBaseEyeHeight;
local float saveBaseEyeHeight;
// TODO:
// TODO:
//
//
// Okay, we need to save the base eye height right now becase it's
// Okay, we need to save the base eye height right now becase it's
// obliterated in Pawn.uc with the following:
// obliterated in Pawn.uc with the following:
//
//
// EyeHeight = 0.8 * CollisionHeight; //FIXME - if all baseeyeheights set right, use them
// EyeHeight = 0.8 * CollisionHeight; //FIXME - if all baseeyeheights set right, use them
// BaseEyeHeight = EyeHeight;
// BaseEyeHeight = EyeHeight;
//
//
// This must be fixed after ECTS.
// This must be fixed after ECTS.
saveBaseEyeHeight = BaseEyeHeight;
saveBaseEyeHeight = BaseEyeHeight;
Super.PreBeginPlay();
Super.PreBeginPlay();
if (!bIsFemale)
if (!bIsFemale)
bIsFemale = bMakeFemale; //GMDX
bIsFemale = bMakeFemale; //GMDX
BaseEyeHeight = saveBaseEyeHeight;
BaseEyeHeight = saveBaseEyeHeight;
// create our shadow
// create our shadow
CreateShadow();
CreateShadow();
// Set our alliance
// Set our alliance
SetAlliance(Alliance);
SetAlliance(Alliance);
// Set up callbacks
// Set up callbacks
UpdateReactionCallbacks();
UpdateReactionCallbacks();
}
}
function UpdateHDTPsettings()
function UpdateHDTPsettings()
{
{
local mesh tempmesh;
local mesh tempmesh;
local texture temptex;
local texture temptex;
local int i;
local int i;
local bool bSetHDTP;
local bool bSetHDTP;
local deusexplayer P;
local deusexplayer P;
P = deusexplayer(getplayerpawn());
P = deusexplayer(getplayerpawn());
if(P != none) //sigh, ok so...I guess I should've thought this through. Pawns fucked with in editor etc now also reset to defaults
if(P != none) //sigh, ok so...I guess I should've thought this through. Pawns fucked with in editor etc now also reset to defaults
{
{
bSetHDTP = P.GetHDTPSettings(self);
bSetHDTP = P.GetHDTPSettings(self);
if(bSetHDTP && !bUsingHDTP)
if(bSetHDTP && !bUsingHDTP)
{
{
if(HDTPMeshname != "")
if(HDTPMeshname != "")
{
{
tempmesh = lodmesh(dynamicloadobject(HDTPMeshname,class'mesh',true));
tempmesh = lodmesh(dynamicloadobject(HDTPMeshname,class'mesh',true));
if(tempmesh != none)
if(tempmesh != none)
{
{
mesh = tempmesh;
mesh = tempmesh;
texture=none;
texture=none;
skin=none;
skin=none;
for(i=0;i<=7;i++)
for(i=0;i<=7;i++)
{
{
if(HDTPMeshtex[i] != "")
if(HDTPMeshtex[i] != "")
{
{
temptex = texture(dynamicloadobject(HDTPMeshtex[i],class'texture',true));
temptex = texture(dynamicloadobject(HDTPMeshtex[i],class'texture',true));
if(temptex != none)
if(temptex != none)
multiskins[i] = temptex;
multiskins[i] = temptex;
}
}
}
}
}
}
}
}
bUsingHDTP=true;
bUsingHDTP=true;
}
}
else if(!bSetHDTP && bUsingHDTP)
else if(!bSetHDTP && bUsingHDTP)
{
{
mesh = default.mesh;
mesh = default.mesh;
texture=default.texture;
texture=default.texture;
skin=default.skin;
skin=default.skin;
for(i=0; i<=7;i++)
for(i=0; i<=7;i++)
{
{
multiskins[i]=default.multiskins[i];
multiskins[i]=default.multiskins[i];
}
}
bUsingHDTP=false;
bUsingHDTP=false;
}
}
}
}
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// PostBeginPlay()
// PostBeginPlay()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function PostBeginPlay()
function PostBeginPlay()
{
{
Super.PostBeginPlay();
Super.PostBeginPlay();
//sort out HDTP settings
//sort out HDTP settings
UpdateHDTPSettings();
UpdateHDTPSettings();
// Set up pain timer
// Set up pain timer
if (Region.Zone.bPainZone || HeadRegion.Zone.bPainZone ||
if (Region.Zone.bPainZone || HeadRegion.Zone.bPainZone ||
FootRegion.Zone.bPainZone)
FootRegion.Zone.bPainZone)
PainTime = 5.0;
PainTime = 5.0;
else if (HeadRegion.Zone.bWaterZone)
else if (HeadRegion.Zone.bWaterZone)
PainTime = UnderWaterTime;
PainTime = UnderWaterTime;
// Handle holograms
// Handle holograms
if ((Style != STY_Masked) && (Style != STY_Normal))
if ((Style != STY_Masked) && (Style != STY_Normal))
{
{
SetSkinStyle(Style, None);
SetSkinStyle(Style, None);
SetCollision(false, false, false);
SetCollision(false, false, false);
KillShadow();
KillShadow();
bHasShadow = False;
bHasShadow = False;
}
}
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// PostPostBeginPlay()
// PostPostBeginPlay()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function PostPostBeginPlay()
function PostPostBeginPlay()
{
{
Super.PostPostBeginPlay();
Super.PostPostBeginPlay();
// Bind any conversation events to this ScriptedPawn
// Bind any conversation events to this ScriptedPawn
ConBindEvents();
ConBindEvents();
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// Destroyed()
// Destroyed()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
simulated function Destroyed()
simulated function Destroyed()
{
{
local DeusExPlayer player;
local DeusExPlayer player;
// Pass a message to conPlay, if it exists in the player, that
// Pass a message to conPlay, if it exists in the player, that
// this pawn has been destroyed. This is used to prevent
// this pawn has been destroyed. This is used to prevent
// bad things from happening in converseations.
// bad things from happening in converseations.
player = DeusExPlayer(GetPlayerPawn());
player = DeusExPlayer(GetPlayerPawn());
if ((player != None) && (player.conPlay != None))
if ((player != None) && (player.conPlay != None))
player.conPlay.ActorDestroyed(Self);
player.conPlay.ActorDestroyed(Self);
Super.Destroyed();
Super.Destroyed();
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// GENERAL UTILITIES
// GENERAL UTILITIES
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// InitializePawn()
// InitializePawn()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function InitializePawn()
function InitializePawn()
{
{
if (!bInitialized)
if (!bInitialized)
{
{
InitializeInventory();
InitializeInventory();
InitializeAlliances();
InitializeAlliances();
InitializeHomeBase();
InitializeHomeBase();
BlockReactions();
BlockReactions();
if (Alliance != '')
if (Alliance != '')
ChangeAlly(Alliance, 1.0, true);
ChangeAlly(Alliance, 1.0, true);
if (!bInWorld)
if (!bInWorld)
{
{
// tricky
// tricky
bInWorld = true;
bInWorld = true;
LeaveWorld();
LeaveWorld();
}
}
// hack!
// hack!
animTimer[1] = 20.0;
animTimer[1] = 20.0;
PlayTurnHead(LOOK_Forward, 1.0, 0.0001);
PlayTurnHead(LOOK_Forward, 1.0, 0.0001);
bInitialized = true;
bInitialized = true;
}
}
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// InitializeInventory()
// InitializeInventory()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function InitializeInventory()
function InitializeInventory()
{
{
local int i, j;
local int i, j;
local Inventory inv;
local Inventory inv;
local Weapon weapons[8];
local Weapon weapons[8];
local int weaponCount;
local int weaponCount;
local Weapon firstWeapon;
local Weapon firstWeapon;
// Add initial inventory items
// Add initial inventory items
weaponCount = 0;
weaponCount = 0;
for (i=0; i<8; i++)
for (i=0; i<8; i++)
{
{
if ((InitialInventory[i].Inventory != None) && (InitialInventory[i].Count > 0))
if ((InitialInventory[i].Inventory != None) && (InitialInventory[i].Count > 0))
{
{
firstWeapon = None;
firstWeapon = None;
for (j=0; j<InitialInventory[i].Count; j++)
for (j=0; j<InitialInventory[i].Count; j++)
{
{
inv = None;
inv = None;
if (Class<Ammo>(InitialInventory[i].Inventory) != None)
if (Class<Ammo>(InitialInventory[i].Inventory) != None)
{
{
inv = FindInventoryType(InitialInventory[i].Inventory);
inv = FindInventoryType(InitialInventory[i].Inventory);
if (inv != None)
if (inv != None)
Ammo(inv).AmmoAmount += Class<Ammo>(InitialInventory[i].Inventory).default.AmmoAmount;
Ammo(inv).AmmoAmount += Class<Ammo>(InitialInventory[i].Inventory).default.AmmoAmount;
}
}
if (inv == None)
if (inv == None)
{
{
inv = spawn(InitialInventory[i].Inventory, self);
inv = spawn(InitialInventory[i].Inventory, self);
if (inv != None)
if (inv != None)
{
{
inv.InitialState='Idle2';
inv.InitialState='Idle2';
inv.GiveTo(Self);
inv.GiveTo(Self);
inv.SetBase(Self);
inv.SetBase(Self);
if ((firstWeapon == None) && (Weapon(inv) != None))
if ((firstWeapon == None) && (Weapon(inv) != None))
firstWeapon = Weapon(inv);
firstWeapon = Weapon(inv);
}
}
}
}
}
}
if (firstWeapon != None)
if (firstWeapon != None)
weapons[WeaponCount++] = firstWeapon;
weapons[WeaponCount++] = firstWeapon;
}
}
}
}
for (i=0; i<weaponCount; i++)
for (i=0; i<weaponCount; i++)
{
{
if ((weapons[i].AmmoType == None) && (weapons[i].AmmoName != None) &&
if ((weapons[i].AmmoType == None) && (weapons[i].AmmoName != None) &&
(weapons[i].AmmoName != Class'AmmoNone'))
(weapons[i].AmmoName != Class'AmmoNone'))
{
{
weapons[i].AmmoType = Ammo(FindInventoryType(weapons[i].AmmoName));
weapons[i].AmmoType = Ammo(FindInventoryType(weapons[i].AmmoName));
if (weapons[i].AmmoType == None)
if (weapons[i].AmmoType == None)
{
{
weapons[i].AmmoType = spawn(weapons[i].AmmoName);
weapons[i].AmmoType = spawn(weapons[i].AmmoName);
weapons[i].AmmoType.InitialState='Idle2';
weapons[i].AmmoType.InitialState='Idle2';
weapons[i].AmmoType.GiveTo(Self);
weapons[i].AmmoType.GiveTo(Self);
weapons[i].AmmoType.SetBase(Self);
weapons[i].AmmoType.SetBase(Self);
}
}
}
}
}
}
SetupWeapon(false);
SetupWeapon(false);
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// InitializeAlliances()
// InitializeAlliances()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function InitializeAlliances()
function InitializeAlliances()
{
{
local int i;
local int i;
for (i=0; i<8; i++)
for (i=0; i<8; i++)
if (InitialAlliances[i].AllianceName != '')
if (InitialAlliances[i].AllianceName != '')
ChangeAlly(InitialAlliances[i].AllianceName,
ChangeAlly(InitialAlliances[i].AllianceName,
InitialAlliances[i].AllianceLevel,
InitialAlliances[i].AllianceLevel,
InitialAlliances[i].bPermanent);
InitialAlliances[i].bPermanent);
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// InitializeHomeBase()
// InitializeHomeBase()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function InitializeHomeBase()
function InitializeHomeBase()
{
{
if (!bUseHome)
if (!bUseHome)
{
{
HomeActor = None;
HomeActor = None;
HomeLoc = Location;
HomeLoc = Location;
HomeRot = vector(Rotation);
HomeRot = vector(Rotation);
if (HomeTag == 'Start')
if (HomeTag == 'Start')
bUseHome = true;
bUseHome = true;
else
else
{
{
HomeActor = HomeBase(FindTaggedActor(HomeTag, , Class'HomeBase'));
HomeActor = HomeBase(FindTaggedActor(HomeTag, , Class'HomeBase'));
if (HomeActor != None)
if (HomeActor != None)
{
{
HomeLoc = HomeActor.Location;
HomeLoc = HomeActor.Location;
HomeRot = vector(HomeActor.Rotation);
HomeRot = vector(HomeActor.Rotation);
HomeExtent = HomeActor.Extent;
HomeExtent = HomeActor.Extent;
bUseHome = true;
bUseHome = true;
}
}
}
}
HomeRot *= 100;
HomeRot *= 100;
}
}
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// AddInitialInventory()
// AddInitialInventory()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function bool AddInitialInventory(class<Inventory> newInventory,
function bool AddInitialInventory(class<Inventory> newInventory,
optional int newCount)
optional int newCount)
{
{
local int i;
local int i;
if (newCount == 0)
if (newCount == 0)
newCount = 1;
newCount = 1;
for (i=0; i<8; i++)
for (i=0; i<8; i++)
if ((InitialInventory[i].Inventory == None) &&
if ((InitialInventory[i].Inventory == None) &&
(InitialInventory[i].Count <= 0))
(InitialInventory[i].Count <= 0))
break;
break;
if (i < 8)
if (i < 8)
{
{
InitialInventory[i].Inventory = newInventory;
InitialInventory[i].Inventory = newInventory;
InitialInventory[i].Count = newCount;
InitialInventory[i].Count = newCount;
return true;
return true;
}
}
else
else
return false;
return false;
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// SetEnemy()
// SetEnemy()
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
function bool SetEnemy(Pawn newEnemy, optional float newSeenTime,
function bool SetEnemy(Pawn newEnemy, optional float newSeenTime,
optional bool bForce)
optional bool bForce)
{
{
if (bForce || IsValidEnemy(newEnemy))
if (bForce || IsValidEnemy(newEnemy))
{
{
if (newEnemy != Enemy)
if (newEnemy != Enemy)
EnemyTimer = 0;
EnemyTimer = 0;
Enemy = newEnemy;
Enemy = newEnemy;
EnemyLastSeen = newSeenTime;
EnemyLastSeen = newSeenTime;
return True;
return True;
}
}
else
else
return False;
return False;
}
}
//
//
保存された差分
原文
ファイルを開く
//============================================================================= // ScriptedPawn. //============================================================================= class ScriptedPawn expands Pawn abstract native; // ---------------------------------------------------------------------- // Enumerations enum EDestinationType { DEST_Failure, DEST_NewLocation, DEST_SameLocation }; enum EAllianceType { ALLIANCE_Friendly, ALLIANCE_Neutral, ALLIANCE_Hostile }; enum ERaiseAlarmType { RAISEALARM_BeforeAttacking, RAISEALARM_BeforeFleeing, RAISEALARM_Never }; enum ESeekType { SEEKTYPE_None, SEEKTYPE_Sound, SEEKTYPE_Sight, SEEKTYPE_Guess, SEEKTYPE_Carcass }; enum EHitLocation { HITLOC_None, HITLOC_HeadFront, HITLOC_HeadBack, HITLOC_TorsoFront, HITLOC_TorsoBack, HITLOC_LeftLegFront, HITLOC_LeftLegBack, HITLOC_RightLegFront, HITLOC_RightLegBack, HITLOC_LeftArmFront, HITLOC_LeftArmBack, HITLOC_RightArmFront, HITLOC_RightArmBack }; enum ETurning { TURNING_None, TURNING_Left, TURNING_Right }; // ---------------------------------------------------------------------- // Structures struct WanderCandidates { var WanderPoint point; var Actor waypoint; var float dist; }; struct FleeCandidates { var HidePoint point; var Actor waypoint; var Vector location; var float score; var float dist; }; struct NearbyProjectile { var DeusExProjectile projectile; var vector location; var float dist; var float range; }; struct NearbyProjectileList { var NearbyProjectile list[8]; var vector center; }; struct InitialAllianceInfo { var() Name AllianceName; var() float AllianceLevel; var() bool bPermanent; }; struct AllianceInfoEx { var Name AllianceName; var float AllianceLevel; var float AgitationLevel; var bool bPermanent; }; struct InventoryItem { var() class<Inventory> Inventory; var() int Count; }; // ---------------------------------------------------------------------- // Variables var WanderPoint lastPoints[2]; var float Restlessness; // 0-1 var float Wanderlust; // 0-1 var float Cowardice; // 0-1 var(Combat) float BaseAccuracy; // 0-1 or thereabouts var(Combat) float MaxRange; var(Combat) float MinRange; var(Combat) float MinHealth; var(AI) float RandomWandering; // 0-1 var float sleepTime; var Actor destPoint; var Vector destLoc; var Vector useLoc; var Rotator useRot; var float seekDistance; // OBSOLETE var int SeekLevel; var ESeekType SeekType; var Pawn SeekPawn; var float CarcassTimer; var float CarcassHateTimer; // OBSOLETE var float CarcassCheckTimer; var name PotentialEnemyAlliance; var float PotentialEnemyTimer; var float BeamCheckTimer; var bool bSeekPostCombat; var bool bSeekLocation; var bool bInterruptSeek; var bool bAlliancesChanged; // set to True whenever someone changes AlliancesEx[i].AllianceLevel to indicate we must do alliance updating var bool bNoNegativeAlliances; // True means we know all alliances are currently +, allows us to skip CheckEnemyPresence's slow part var bool bSitAnywhere; var bool bSitInterpolation; var bool bStandInterpolation; var float remainingSitTime; var float remainingStandTime; var vector StandRate; var float ReloadTimer; var bool bReadyToReload; var(Pawn) class<carcass> CarcassType; // mesh to use when killed from the front // Advanced AI attributes. var(Orders) name Orders; // orders a creature is carrying out // will be initial state, plus creature will attempt // to return to this state var(Orders) name OrderTag; // tag of object referred to by orders var(Orders) name HomeTag; // tag of object to use as home base var(Orders) float HomeExtent; // extent of home base var actor OrderActor; // object referred to by orders (if applicable) var name NextAnim; // used in states with multiple, sequenced animations var float WalkingSpeed; // 0-1 var(Combat) float ProjectileSpeed; var name LastPainAnim; var float LastPainTime; var vector DesiredPrePivot; var float PrePivotTime; var vector PrePivotOffset; var bool bCanBleed; // true if an NPC can bleed var float BleedRate; // how profusely the NPC is bleeding; 0-1 var float DropCounter; // internal; used in tick() var() float ClotPeriod; // seconds it takes bleedRate to go from 1 to 0 var bool bAcceptBump; // ugly hack var bool bCanFire; // true if pawn is capable of shooting asynchronously var(AI) bool bKeepWeaponDrawn; // true if pawn should always keep weapon drawn var(AI) bool bShowPain; // true if pawn should play pain animations var(AI) bool bCanSit; // true if pawn can sit var(AI) bool bAlwaysPatrol; // true if stasis should be disabled during patrols var(AI) bool bPlayIdle; // true if pawn should fidget while he's standing var(AI) bool bLeaveAfterFleeing; // true if pawn should disappear after fleeing var(AI) bool bLikesNeutral; // true if pawn should treat neutrals as friendlies var(AI) bool bUseFirstSeatOnly; // true if only the nearest chair should be used for var(AI) bool bCower; // true if fearful pawns should cower instead of fleeing var(AI) bool bMakeFemale; // true if pawn is female var HomeBase HomeActor; // home base var Vector HomeLoc; // location of home base var Vector HomeRot; // rotation of home base var bool bUseHome; // true if home base should be used var bool bInterruptState; // true if the state can be interrupted var bool bCanConverse; // true if the pawn can converse var() bool bImportant; // true if this pawn is game-critical var() bool bInvincible; // true if this pawn cannot be killed var bool bInitialized; // true if this pawn has been initialized var(Combat) bool bAvoidAim; // avoid enemy's line of fire var(Combat) bool bAimForHead; // aim for the enemy's head var(Combat) bool bDefendHome; // defend the home base var bool bCanCrouch; // whether we should crouch when firing var bool bSeekCover; // seek cover var bool bSprint; // sprint in random directions var(Combat) bool bUseFallbackWeapons; // use fallback weapons even when others are available var float AvoidAccuracy; // how well we avoid enemy's line of fire; 0-1 var bool bAvoidHarm; // avoid painful projectiles, gas clouds, etc. var float HarmAccuracy; // how well we avoid harm; 0-1 var float CrouchRate; // how often the NPC crouches, if bCrouch enabled; 0-1 var float SprintRate; // how often the NPC randomly sprints if bSprint enabled; 0-1 var float CloseCombatMult; // multiplier for how often the NPC sprints in close combat; 0-1 // If a stimulation is enabled, it causes an NPC to hate the stimulator //var(Stimuli) bool bHateFutz; var(Stimuli) bool bHateHacking; // new var(Stimuli) bool bHateWeapon; var(Stimuli) bool bHateShot; var(Stimuli) bool bHateInjury; var(Stimuli) bool bHateIndirectInjury; // new //var(Stimuli) bool bHateGore; var(Stimuli) bool bHateCarcass; // new var(Stimuli) bool bHateDistress; //var(Stimuli) bool bHateProjectiles; // If a reaction is enabled, the NPC will react appropriately to a stimulation var(Reactions) bool bReactFutz; // new var(Reactions) bool bReactPresence; // React to the presence of an enemy (attacking) var(Reactions) bool bReactLoudNoise; // Seek the source of a loud noise (seeking) var(Reactions) bool bReactAlarm; // Seek the source of an alarm (seeking) var(Reactions) bool bReactShot; // React to a gunshot fired by an enemy (attacking) //var(Reactions) bool bReactGore; // React to gore appropriately (seeking) var(Reactions) bool bReactCarcass; // React to gore appropriately (seeking) var(Reactions) bool bReactDistress; // React to distress appropriately (attacking) var(Reactions) bool bReactProjectiles; // React to harmful projectiles appropriately // If a fear is enabled, the NPC will run away from the stimulator var(Fears) bool bFearHacking; // Run away from a hacker var(Fears) bool bFearWeapon; // Run away from a person holding a weapon var(Fears) bool bFearShot; // Run away from a person who fires a shot var(Fears) bool bFearInjury; // Run away from a person who causes injury var(Fears) bool bFearIndirectInjury; // Run away from a person who causes indirect injury var(Fears) bool bFearCarcass; // Run away from a carcass var(Fears) bool bFearDistress; // Run away from a person causing distress var(Fears) bool bFearAlarm; // Run away from the source of an alarm var(Fears) bool bFearProjectiles; // Run away from a projectile var(AI) bool bEmitDistress; // TRUE if NPC should emit distress var(AI) ERaiseAlarmType RaiseAlarm; // When to raise an alarm var bool bLookingForEnemy; // TRUE if we're actually looking for enemies var bool bLookingForLoudNoise; // TRUE if we're listening for loud noises var bool bLookingForAlarm; // TRUE if we're listening for alarms var bool bLookingForDistress; // TRUE if we're looking for signs of distress var bool bLookingForProjectiles; // TRUE if we're looking for projectiles that can harm us var bool bLookingForFutz; // TRUE if we're looking for people futzing with stuff var bool bLookingForHacking; // TRUE if we're looking for people hacking stuff var bool bLookingForShot; // TRUE if we're listening for gunshots var bool bLookingForWeapon; // TRUE if we're looking for drawn weapons var bool bLookingForCarcass; // TRUE if we're looking for carcass events var bool bLookingForInjury; // TRUE if we're looking for injury events var bool bLookingForIndirectInjury; // TRUE if we're looking for secondary injury events var bool bFacingTarget; // True if pawn is facing its target var(Combat) bool bMustFaceTarget; // True if an NPC must face his target to fire var(Combat) float FireAngle; // TOTAL angle (in degrees) in which a pawn may fire if bMustFaceTarget is false var(Combat) float FireElevation; // Max elevation distance required to attack (0=elevation doesn't matter) var(AI) int MaxProvocations; var float AgitationSustainTime; var float AgitationDecayRate; var float AgitationTimer; var float AgitationCheckTimer; var float PlayerAgitationTimer; // hack var float FearSustainTime; var float FearDecayRate; var float FearTimer; var float FearLevel; var float EnemyReadiness; var float ReactionLevel; var float SurprisePeriod; var float SightPercentage; var float CycleTimer; var float CyclePeriod; var float CycleCumulative; var Pawn CycleCandidate; var float CycleDistance; var AlarmUnit AlarmActor; var float AlarmTimer; var float WeaponTimer; var float FireTimer; var float SpecialTimer; var float CrouchTimer; var float BackpedalTimer; var bool bHasShadow; var float ShadowScale; var bool bDisappear; var bool bInTransientState; // true if the NPC is in a 3rd-tier (transient) state, like TakeHit var(Alliances) InitialAllianceInfo InitialAlliances[8]; var AllianceInfoEx AlliancesEx[16]; var bool bReverseAlliances; var(Pawn) float BaseAssHeight; var(AI) float EnemyTimeout; var float CheckPeriod; var float EnemyLastSeen; var int SeatSlot; var Seat SeatActor; var int CycleIndex; var int BodyIndex; var bool bRunningStealthy; var bool bPausing; var bool bStaring; var bool bAttacking; var bool bDistressed; var bool bStunned; var bool bSitting; var bool bDancing; var bool bCrouching; var bool bCanTurnHead; var(AI) bool bTickVisibleOnly; // Temporary? var() bool bInWorld; var vector WorldPosition; var bool bWorldCollideActors; var bool bWorldBlockActors; var bool bWorldBlockPlayers; var() bool bHighlight; // should this object not highlight when focused? var(AI) bool bHokeyPokey; var bool bConvEndState; var(Inventory) InventoryItem InitialInventory[8]; // Initial inventory items carried by the pawn var Bool bConversationEndedNormally; var Bool bInConversation; var Actor ConversationActor; // Actor currently speaking to or speaking to us var() sound WalkSound; var float swimBubbleTimer; var bool bSpawnBubbles; var bool bUseSecondaryAttack; var bool bWalkAround; var bool bClearedObstacle; var bool bEnableCheckDest; var ETurning TurnDirection; var ETurning NextDirection; var Actor ActorAvoiding; var float AvoidWallTimer; var float AvoidBumpTimer; var float ObstacleTimer; var vector LastDestLoc; var vector LastDestPoint; var int DestAttempts; var float DeathTimer; var float EnemyTimer; var float TakeHitTimer; var name ConvOrders; var name ConvOrderTag; var float BurnPeriod; var float FutzTimer; var float DistressTimer; var vector SeatLocation; var Seat SeatHack; var bool bSeatLocationValid; var bool bSeatHackUsed; var bool bBurnedToDeath; var bool bHasCloak; var bool bCloakOn; var int CloakThreshold; var float CloakEMPTimer; var float poisonTimer; // time remaining before next poison TakeDamage var int poisonCounter; // number of poison TakeDamages remaining var int poisonDamage; // damage taken from poison effect var Pawn Poisoner; // person who initiated PoisonEffect damage var Name Carcasses[4]; // list of carcasses seen var int NumCarcasses; // number of carcasses seen var float walkAnimMult; var float runAnimMult; var bool bUsingHDTP; var string HDTPMeshName; var string HDTPMeshTex[8]; //GMDX var bool bBurnedUp; //CyberP: for plasma rifle gibbing var bool bFlyer; //CyberP: for pawn knockback var bool bSetupPop; //CyberP: for headpop var int extraMult; //CyberP: variable headshot multiplier var bool bRadialBlastClamp; //CyberP: MiB radial attack var bool bCanFlare; //CyberP: AI can throw flares if conditions are met var bool bHasThrownFlare; //CyberP: has this NPC already thrown a flare? var bool bCanNade; //CyberP: AI can throw nades if conditions are met var bool bGrenadier; //CyberP: this pawn class can throw nades var bool bCommandoMelee; var bool bDefensiveStyle; //CyberP: this pawn doesn't always bum rush the player in close proximity //var bool bFrontPop; //CyberP: for headpop var int disturbanceCount; //CyberP: obsolete. var bool bAlarmStatIncrease; //CyberP: AI get a one-time stat boost if alarm is triggered var bool bHasHelmet; var bool bBiteClamp; var bool bGreaselShould; var(Filter) bool bHardcoreOnly; // CyberP: remove this pawn from world if we are not hardcore. var(AI) bool bCanPop; //CyberP: if we can pop at all. native(2102) final function ConBindEvents(); native(2105) final function bool IsValidEnemy(Pawn TestEnemy, optional bool bCheckAlliance); native(2106) final function EAllianceType GetAllianceType(Name AllianceName); native(2107) final function EAllianceType GetPawnAllianceType(Pawn QueryPawn); native(2108) final function bool HaveSeenCarcass(Name CarcassName); native(2109) final function AddCarcass(Name CarcassName); // ---------------------------------------------------------------------- // PreBeginPlay() // ---------------------------------------------------------------------- function PreBeginPlay() { local float saveBaseEyeHeight; // TODO: // // Okay, we need to save the base eye height right now becase it's // obliterated in Pawn.uc with the following: // // EyeHeight = 0.8 * CollisionHeight; //FIXME - if all baseeyeheights set right, use them // BaseEyeHeight = EyeHeight; // // This must be fixed after ECTS. saveBaseEyeHeight = BaseEyeHeight; Super.PreBeginPlay(); if (!bIsFemale) bIsFemale = bMakeFemale; //GMDX BaseEyeHeight = saveBaseEyeHeight; // create our shadow CreateShadow(); // Set our alliance SetAlliance(Alliance); // Set up callbacks UpdateReactionCallbacks(); } function UpdateHDTPsettings() { local mesh tempmesh; local texture temptex; local int i; local bool bSetHDTP; local deusexplayer P; P = deusexplayer(getplayerpawn()); if(P != none) //sigh, ok so...I guess I should've thought this through. Pawns fucked with in editor etc now also reset to defaults { bSetHDTP = P.GetHDTPSettings(self); if(bSetHDTP && !bUsingHDTP) { if(HDTPMeshname != "") { tempmesh = lodmesh(dynamicloadobject(HDTPMeshname,class'mesh',true)); if(tempmesh != none) { mesh = tempmesh; texture=none; skin=none; for(i=0;i<=7;i++) { if(HDTPMeshtex[i] != "") { temptex = texture(dynamicloadobject(HDTPMeshtex[i],class'texture',true)); if(temptex != none) multiskins[i] = temptex; } } } } bUsingHDTP=true; } else if(!bSetHDTP && bUsingHDTP) { mesh = default.mesh; texture=default.texture; skin=default.skin; for(i=0; i<=7;i++) { multiskins[i]=default.multiskins[i]; } bUsingHDTP=false; } } } // ---------------------------------------------------------------------- // PostBeginPlay() // ---------------------------------------------------------------------- function PostBeginPlay() { Super.PostBeginPlay(); //sort out HDTP settings UpdateHDTPSettings(); // Set up pain timer if (Region.Zone.bPainZone || HeadRegion.Zone.bPainZone || FootRegion.Zone.bPainZone) PainTime = 5.0; else if (HeadRegion.Zone.bWaterZone) PainTime = UnderWaterTime; // Handle holograms if ((Style != STY_Masked) && (Style != STY_Normal)) { SetSkinStyle(Style, None); SetCollision(false, false, false); KillShadow(); bHasShadow = False; } } // ---------------------------------------------------------------------- // PostPostBeginPlay() // ---------------------------------------------------------------------- function PostPostBeginPlay() { Super.PostPostBeginPlay(); // Bind any conversation events to this ScriptedPawn ConBindEvents(); } // ---------------------------------------------------------------------- // Destroyed() // ---------------------------------------------------------------------- simulated function Destroyed() { local DeusExPlayer player; // Pass a message to conPlay, if it exists in the player, that // this pawn has been destroyed. This is used to prevent // bad things from happening in converseations. player = DeusExPlayer(GetPlayerPawn()); if ((player != None) && (player.conPlay != None)) player.conPlay.ActorDestroyed(Self); Super.Destroyed(); } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // GENERAL UTILITIES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // InitializePawn() // ---------------------------------------------------------------------- function InitializePawn() { if (!bInitialized) { InitializeInventory(); InitializeAlliances(); InitializeHomeBase(); BlockReactions(); if (Alliance != '') ChangeAlly(Alliance, 1.0, true); if (!bInWorld) { // tricky bInWorld = true; LeaveWorld(); } // hack! animTimer[1] = 20.0; PlayTurnHead(LOOK_Forward, 1.0, 0.0001); bInitialized = true; } } // ---------------------------------------------------------------------- // InitializeInventory() // ---------------------------------------------------------------------- function InitializeInventory() { local int i, j; local Inventory inv; local Weapon weapons[8]; local int weaponCount; local Weapon firstWeapon; // Add initial inventory items weaponCount = 0; for (i=0; i<8; i++) { if ((InitialInventory[i].Inventory != None) && (InitialInventory[i].Count > 0)) { firstWeapon = None; for (j=0; j<InitialInventory[i].Count; j++) { inv = None; if (Class<Ammo>(InitialInventory[i].Inventory) != None) { inv = FindInventoryType(InitialInventory[i].Inventory); if (inv != None) Ammo(inv).AmmoAmount += Class<Ammo>(InitialInventory[i].Inventory).default.AmmoAmount; } if (inv == None) { inv = spawn(InitialInventory[i].Inventory, self); if (inv != None) { inv.InitialState='Idle2'; inv.GiveTo(Self); inv.SetBase(Self); if ((firstWeapon == None) && (Weapon(inv) != None)) firstWeapon = Weapon(inv); } } } if (firstWeapon != None) weapons[WeaponCount++] = firstWeapon; } } for (i=0; i<weaponCount; i++) { if ((weapons[i].AmmoType == None) && (weapons[i].AmmoName != None) && (weapons[i].AmmoName != Class'AmmoNone')) { weapons[i].AmmoType = Ammo(FindInventoryType(weapons[i].AmmoName)); if (weapons[i].AmmoType == None) { weapons[i].AmmoType = spawn(weapons[i].AmmoName); weapons[i].AmmoType.InitialState='Idle2'; weapons[i].AmmoType.GiveTo(Self); weapons[i].AmmoType.SetBase(Self); } } } SetupWeapon(false); } // ---------------------------------------------------------------------- // InitializeAlliances() // ---------------------------------------------------------------------- function InitializeAlliances() { local int i; for (i=0; i<8; i++) if (InitialAlliances[i].AllianceName != '') ChangeAlly(InitialAlliances[i].AllianceName, InitialAlliances[i].AllianceLevel, InitialAlliances[i].bPermanent); } // ---------------------------------------------------------------------- // InitializeHomeBase() // ---------------------------------------------------------------------- function InitializeHomeBase() { if (!bUseHome) { HomeActor = None; HomeLoc = Location; HomeRot = vector(Rotation); if (HomeTag == 'Start') bUseHome = true; else { HomeActor = HomeBase(FindTaggedActor(HomeTag, , Class'HomeBase')); if (HomeActor != None) { HomeLoc = HomeActor.Location; HomeRot = vector(HomeActor.Rotation); HomeExtent = HomeActor.Extent; bUseHome = true; } } HomeRot *= 100; } } // ---------------------------------------------------------------------- // AddInitialInventory() // ---------------------------------------------------------------------- function bool AddInitialInventory(class<Inventory> newInventory, optional int newCount) { local int i; if (newCount == 0) newCount = 1; for (i=0; i<8; i++) if ((InitialInventory[i].Inventory == None) && (InitialInventory[i].Count <= 0)) break; if (i < 8) { InitialInventory[i].Inventory = newInventory; InitialInventory[i].Count = newCount; return true; } else return false; } // ---------------------------------------------------------------------- // SetEnemy() // ---------------------------------------------------------------------- function bool SetEnemy(Pawn newEnemy, optional float newSeenTime, optional bool bForce) { if (bForce || IsValidEnemy(newEnemy)) { if (newEnemy != Enemy) EnemyTimer = 0; Enemy = newEnemy; EnemyLastSeen = newSeenTime; return True; } else return False; } // ---------------------------------------------------------------------- // SetState() // ---------------------------------------------------------------------- function SetState(Name stateName, optional Name labelName) { if (bInterruptState) GotoState(stateName, labelName); else SetNextState(stateName, labelName); } // ---------------------------------------------------------------------- // SetNextState() // ---------------------------------------------------------------------- function SetNextState(name newState, optional name newLabel) { if (!bInTransientState || !HasNextState()) { if ((newState != 'Conversation') && (newState != 'FirstPersonConversation')) { NextState = newState; NextLabel = newLabel; } } } // ---------------------------------------------------------------------- // ClearNextState() // ---------------------------------------------------------------------- function ClearNextState() { NextState = ''; NextLabel = ''; } // ---------------------------------------------------------------------- // HasNextState() // ---------------------------------------------------------------------- function bool HasNextState() { if ((NextState == '') || (NextState == GetStateName())) return false; else return true; } // ---------------------------------------------------------------------- // GotoNextState() // ---------------------------------------------------------------------- function GotoNextState() { local bool bSuccess; local name oldState, oldLabel; if (HasNextState()) { oldState = NextState; oldLabel = NextLabel; if (oldLabel == '') oldLabel = 'Begin'; ClearNextState(); GotoState(oldState, oldLabel); } else ClearNextState(); } // ---------------------------------------------------------------------- // SetOrders() // ---------------------------------------------------------------------- function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate) { local bool bHostile; local Pawn orderEnemy; switch (orderName) { case 'Attacking': case 'Fleeing': case 'Alerting': case 'Seeking': bHostile = true; break; default: bHostile = false; break; } if (!bHostile) { bSeatHackUsed = false; // hack! Orders = orderName; OrderTag = newOrderTag; if (bImmediate) FollowOrders(true); } else { ReactionLevel = 1.0; orderEnemy = Pawn(FindTaggedActor(newOrderTag, false, Class'Pawn')); if (orderEnemy != None) { ChangeAlly(orderEnemy.Alliance, -1, true); if (SetEnemy(orderEnemy)) SetState(orderName); } } } // ---------------------------------------------------------------------- // SetHomeBase() // ---------------------------------------------------------------------- function SetHomeBase(vector baseLocation, optional rotator baseRotator, optional float baseExtent) { local vector vectRot; if (baseExtent == 0) baseExtent = 800; HomeTag = ''; HomeActor = None; HomeLoc = baseLocation; HomeRot = vector(baseRotator)*100; HomeExtent = baseExtent; bUseHome = true; } // ---------------------------------------------------------------------- // ClearHomeBase() // ---------------------------------------------------------------------- function ClearHomeBase() { HomeTag = ''; bUseHome = false; } // ---------------------------------------------------------------------- // IsSeatValid() // ---------------------------------------------------------------------- function bool IsSeatValid(Actor checkActor) { local PlayerPawn player; local Seat checkSeat; checkSeat = Seat(checkActor); if (checkSeat == None) return false; else if (checkSeat.bDeleteMe) return false; else if (!bSitAnywhere && (VSize(checkSeat.Location-checkSeat.InitialPosition) > 70)) return false; else { player = GetPlayerPawn(); if (player != None) { if (player.CarriedDecoration == checkSeat) return false; } return true; } } // ---------------------------------------------------------------------- // SetDistress() // ---------------------------------------------------------------------- function SetDistress(bool bDistress) { bDistressed = bDistress; if (bDistress && bEmitDistress) AIStartEvent('Distress', EAITYPE_Visual); else AIEndEvent('Distress', EAITYPE_Visual); } function ShouldGreaselMelee() //CyberP: somehow gets called out of the desired state and crashes, so this prevents that. { } // ---------------------------------------------------------------------- // SetDistressTimer() // ---------------------------------------------------------------------- function SetDistressTimer() { DistressTimer = 0; } // ---------------------------------------------------------------------- // SetSeekLocation() // ---------------------------------------------------------------------- function SetSeekLocation(Pawn seekCandidate, vector newLocation, ESeekType newSeekType, optional bool bNewPostCombat) { SetEnemy(None, 0, true); SeekPawn = seekCandidate; LastSeenPos = newLocation; bSeekLocation = True; SeekType = newSeekType; if (newSeekType == SEEKTYPE_Carcass) CarcassTimer = 120.0; if (newSeekType == SEEKTYPE_Sight) SeekLevel = Max(SeekLevel, 1); else SeekLevel = Max(SeekLevel, 3); if (bNewPostCombat) bSeekPostCombat = true; } // ---------------------------------------------------------------------- // GetCarcassData() // ---------------------------------------------------------------------- function bool GetCarcassData(actor sender, out Name killer, out Name alliance, out Name CarcassName, optional bool bCheckName) { local DeusExPlayer dxPlayer; local DeusExCarcass carcass; local POVCorpse corpseItem; local bool bCares; local bool bValid; alliance = ''; killer = ''; bValid = false; dxPlayer = DeusExPlayer(sender); carcass = DeusExCarcass(sender); if (dxPlayer != None) { corpseItem = POVCorpse(dxPlayer.inHand); if (corpseItem != None) { if (corpseItem.bEmitCarcass) { alliance = corpseItem.Alliance; killer = corpseItem.KillerAlliance; CarcassName = corpseItem.CarcassName; bValid = true; } } } else if (carcass != None) { if (carcass.bEmitCarcass) { alliance = carcass.Alliance; killer = carcass.KillerAlliance; CarcassName = carcass.CarcassName; bValid = true; } } bCares = false; if (bValid && (!bCheckName || !HaveSeenCarcass(CarcassName))) { if (bFearCarcass) bCares = true; else { if (GetAllianceType(alliance) == ALLIANCE_Friendly) { if (bHateCarcass) bCares = true; else if (bReactCarcass) { if (GetAllianceType(killer) == ALLIANCE_Hostile) bCares = true; } } } } return bCares; } // ---------------------------------------------------------------------- // ReactToFutz() // ---------------------------------------------------------------------- function ReactToFutz() { if (bLookingForFutz && bReactFutz && (FutzTimer <= 0) && !bDistressed) { FutzTimer = 2.0; PlayFutzSound(); } } // ---------------------------------------------------------------------- // ReactToProjectiles() // ---------------------------------------------------------------------- function ReactToProjectiles(Actor projectileActor) { local DeusExProjectile dxProjectile; local Pawn instigator; if ((bFearProjectiles || bReactProjectiles) && bLookingForProjectiles) { dxProjectile = DeusExProjectile(projectileActor); if ((dxProjectile == None) || IsProjectileDangerous(dxProjectile)) { instigator = Pawn(projectileActor); if (instigator == None) instigator = projectileActor.Instigator; if (instigator != None) { if (bFearProjectiles) IncreaseFear(instigator, 2.0); if (SetEnemy(instigator)) { SetDistressTimer(); HandleEnemy(); } else if (bFearProjectiles && IsFearful()) { SetDistressTimer(); SetEnemy(instigator, , true); GotoState('Fleeing'); } else if (bAvoidHarm) SetState('AvoidingProjectiles'); } } } } // ---------------------------------------------------------------------- // InstigatorToPawn() // ---------------------------------------------------------------------- function Pawn InstigatorToPawn(Actor eventActor) { local Pawn pawnActor; if (Inventory(eventActor) != None) { if (Inventory(eventActor).Owner != None) eventActor = Inventory(eventActor).Owner; } else if (DeusExDecoration(eventActor) != None) eventActor = GetPlayerPawn(); else if (DeusExProjectile(eventActor) != None) eventActor = eventActor.Instigator; pawnActor = Pawn(eventActor); if (pawnActor == self) pawnActor = None; return pawnActor; } // ---------------------------------------------------------------------- // EnableShadow() // ---------------------------------------------------------------------- function EnableShadow(bool bEnable) { if (Shadow != None) { if (bEnable) Shadow.AttachDecal(32,vect(0.1,0.1,0)); else Shadow.DetachDecal(); } } // ---------------------------------------------------------------------- // CreateShadow() // ---------------------------------------------------------------------- function CreateShadow() { if (bHasShadow && bInWorld) if (Shadow == None) Shadow = Spawn(class'Shadow', Self,, Location-vect(0,0,1)*CollisionHeight, rot(16384,0,0)); } // ---------------------------------------------------------------------- // KillShadow() // ---------------------------------------------------------------------- function KillShadow() { if (Shadow != None) { Shadow.Destroy(); Shadow = None; } } // ---------------------------------------------------------------------- // EnterWorld() // ---------------------------------------------------------------------- function EnterWorld() { PutInWorld(true); } // ---------------------------------------------------------------------- // LeaveWorld() // ---------------------------------------------------------------------- function LeaveWorld() { PutInWorld(false); } // ---------------------------------------------------------------------- // PutInWorld() // ---------------------------------------------------------------------- function PutInWorld(bool bEnter) { if (bInWorld && !bEnter) { bInWorld = false; GotoState('Idle'); bHidden = true; bDetectable = false; WorldPosition = Location; bWorldCollideActors = bCollideActors; bWorldBlockActors = bBlockActors; bWorldBlockPlayers = bBlockPlayers; SetCollision(false, false, false); bCollideWorld = false; SetPhysics(PHYS_None); KillShadow(); SetLocation(Location+vect(0,0,20000)); // move it out of the way } else if (!bInWorld && bEnter) { bInWorld = true; bHidden = Default.bHidden; bDetectable = Default.bDetectable; SetLocation(WorldPosition); SetCollision(bWorldCollideActors, bWorldBlockActors, bWorldBlockPlayers); bCollideWorld = Default.bCollideWorld; SetMovementPhysics(); CreateShadow(); FollowOrders(); } } // ---------------------------------------------------------------------- // MakePawnIgnored() // ---------------------------------------------------------------------- function MakePawnIgnored(bool bNewIgnore) { if (bNewIgnore) { bIgnore = bNewIgnore; // to restore original behavior, uncomment the next line //bDetectable = !bNewIgnore; } else { bIgnore = Default.bIgnore; // to restore original behavior, uncomment the next line //bDetectable = Default.bDetectable; } } // ---------------------------------------------------------------------- // EnableCollision() [for sitting state] // ---------------------------------------------------------------------- function EnableCollision(bool bSet) { EnableShadow(bSet); if (bSet) SetCollision(Default.bCollideActors, Default.bBlockActors, Default.bBlockPlayers); else SetCollision(True, False, True); } // ---------------------------------------------------------------------- // SetBasedPawnSize() // ---------------------------------------------------------------------- function bool SetBasedPawnSize(float newRadius, float newHeight) { local float oldRadius, oldHeight; local bool bSuccess; local vector centerDelta; local float deltaEyeHeight; if (newRadius < 0) newRadius = 0; if (newHeight < 0) newHeight = 0; oldRadius = CollisionRadius; oldHeight = CollisionHeight; if ((oldRadius == newRadius) && (oldHeight == newHeight)) return true; centerDelta = vect(0, 0, 1)*(newHeight-oldHeight); deltaEyeHeight = GetDefaultCollisionHeight() - Default.BaseEyeHeight; bSuccess = false; if ((newHeight <= CollisionHeight) && (newRadius <= CollisionRadius)) // shrink { SetCollisionSize(newRadius, newHeight); if (Move(centerDelta)) bSuccess = true; else SetCollisionSize(oldRadius, oldHeight); } else { if (Move(centerDelta)) { SetCollisionSize(newRadius, newHeight); bSuccess = true; } } if (bSuccess) { PrePivotOffset = vect(0, 0, 1)*(GetDefaultCollisionHeight()-newHeight); PrePivot -= centerDelta; DesiredPrePivot -= centerDelta; BaseEyeHeight = newHeight - deltaEyeHeight; } return (bSuccess); } // ---------------------------------------------------------------------- // ResetBasedPawnSize() // ---------------------------------------------------------------------- function ResetBasedPawnSize() { SetBasedPawnSize(Default.CollisionRadius, GetDefaultCollisionHeight()); } // ---------------------------------------------------------------------- // GetDefaultCollisionHeight() // ---------------------------------------------------------------------- function float GetDefaultCollisionHeight() { return (Default.CollisionHeight-4.5); } // ---------------------------------------------------------------------- // GetCrouchHeight() // ---------------------------------------------------------------------- function float GetCrouchHeight() { return (Default.CollisionHeight*0.65); } // ---------------------------------------------------------------------- // GetSitHeight() // ---------------------------------------------------------------------- function float GetSitHeight() { return (GetDefaultCollisionHeight()+(BaseAssHeight*0.5)); } // ---------------------------------------------------------------------- // IsPointInCylinder() // ---------------------------------------------------------------------- function bool IsPointInCylinder(Actor cylinder, Vector point, optional float extraRadius, optional float extraHeight) { local bool bPointInCylinder; local float tempX, tempY, tempRad; tempX = cylinder.Location.X - point.X; tempX *= tempX; tempY = cylinder.Location.Y - point.Y; tempY *= tempY; tempRad = cylinder.CollisionRadius + extraRadius; tempRad *= tempRad; bPointInCylinder = false; if (tempX+tempY < tempRad) if (Abs(cylinder.Location.Z - point.Z) < (cylinder.CollisionHeight+extraHeight)) bPointInCylinder = true; return (bPointInCylinder); } // ---------------------------------------------------------------------- // StartFalling() // ---------------------------------------------------------------------- function StartFalling(Name resumeState, optional Name resumeLabel) { SetNextState(resumeState, resumeLabel); GotoState('FallingState'); } // ---------------------------------------------------------------------- // GetNextWaypoint() // ---------------------------------------------------------------------- function Actor GetNextWaypoint(Actor destination) { local Actor moveTarget; if (destination == None) moveTarget = None; else if (ActorReachable(destination)) moveTarget = destination; else moveTarget = FindPathToward(destination); return (moveTarget); } // ---------------------------------------------------------------------- // GetNextVector() // ---------------------------------------------------------------------- function bool GetNextVector(Actor destination, out vector outVect) { local bool bValid; local rotator rot; local float dist; local float maxDist; bValid = true; if (destination != None) { maxDist = 64; rot = Rotator(destination.Location - Location); dist = VSize(destination.Location - Location); if (dist < maxDist) outVect = destination.Location; else if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 0, maxDist, outVect)) bValid = false; } else bValid = false; return (bValid); } // ---------------------------------------------------------------------- // FindOrderActor() // ---------------------------------------------------------------------- function FindOrderActor() { if (Orders == 'Attacking') OrderActor = FindTaggedActor(OrderTag, true, Class'Pawn'); else OrderActor = FindTaggedActor(OrderTag); } // ---------------------------------------------------------------------- // FindTaggedActor() // ---------------------------------------------------------------------- function Actor FindTaggedActor(Name actorTag, optional bool bRandom, optional Class<Actor> tagClass) { local float dist; local float bestDist; local actor bestActor; local actor tempActor; bestActor = None; bestDist = 1000000; if (tagClass == None) tagClass = Class'Actor'; // if no tag, then assume the player is the target if (actorTag == '') bestActor = GetPlayerPawn(); else { foreach AllActors(tagClass, tempActor, actorTag) { if (tempActor != self) { dist = VSize(tempActor.Location - Location); if (bRandom) dist *= FRand()*0.6+0.7; // +/- 30% if ((bestActor == None) || (dist < bestDist)) { bestActor = tempActor; bestDist = dist; } } } } return bestActor; } // ---------------------------------------------------------------------- // HandleEnemy() // ---------------------------------------------------------------------- function HandleEnemy() { SetState('HandlingEnemy', 'Begin'); } // ---------------------------------------------------------------------- // HandleSighting() // ---------------------------------------------------------------------- function HandleSighting(Pawn pawnSighted) { SetSeekLocation(pawnSighted, pawnSighted.Location, SEEKTYPE_Sight); GotoState('Seeking'); } // ---------------------------------------------------------------------- // FollowOrders() // ---------------------------------------------------------------------- function FollowOrders(optional bool bDefer) { local bool bSetEnemy; local bool bUseOrderActor; if (Orders != '') { if ((Orders == 'Fleeing') || (Orders == 'Attacking')) { bSetEnemy = true; bUseOrderActor = true; } else if ((Orders == 'WaitingFor') || (Orders == 'GoingTo') || (Orders == 'RunningTo') || (Orders == 'Following') || (Orders == 'Sitting') || (Orders == 'Shadowing') || (Orders == 'DebugFollowing') || (Orders == 'DebugPathfinding')) { bSetEnemy = false; bUseOrderActor = true; } else { bSetEnemy = false; bUseOrderActor = false; } if (bUseOrderActor) { FindOrderActor(); if (bSetEnemy) SetEnemy(Pawn(OrderActor), 0, true); } if (bDefer) // hack SetState(Orders); else GotoState(Orders); } else { if (bDefer) SetState('Wandering'); else GotoState('Wandering'); } } // ---------------------------------------------------------------------- // ResetConvOrders() // ---------------------------------------------------------------------- function ResetConvOrders() { ConvOrders = ''; ConvOrderTag = ''; } // ---------------------------------------------------------------------- // GenerateTotalHealth() // // this will calculate a weighted average of all of the body parts // and put that value in the generic Health // NOTE: head and torso are both critical // ---------------------------------------------------------------------- function GenerateTotalHealth() { local float limbDamage, headDamage, torsoDamage; if (!bInvincible) { // Scoring works as follows: // Disabling the head (100 points damage) will kill you. // Disabling the torso (100 points damage) will kill you. // Disabling 2 1/2 limbs (250 points damage) will kill you. // Combinations can also do you in -- 50 points damage to the head // and 125 points damage to the limbs, for example. // Note that this formula can produce numbers less than zero, so we'll clamp our // health value... // Compute total limb damage limbDamage = 0; if (Default.HealthLegLeft > 0) limbDamage += float(Default.HealthLegLeft-HealthLegLeft)/Default.HealthLegLeft; if (Default.HealthLegRight > 0) limbDamage += float(Default.HealthLegRight-HealthLegRight)/Default.HealthLegRight; if (Default.HealthArmLeft > 0) limbDamage += float(Default.HealthArmLeft-HealthArmLeft)/Default.HealthArmLeft; if (Default.HealthArmRight > 0) limbDamage += float(Default.HealthArmRight-HealthArmRight)/Default.HealthArmRight; limbDamage *= 0.4; // 2 1/2 limbs disabled == death // Compute total head damage headDamage = 0; if (Default.HealthHead > 0) headDamage = float(Default.HealthHead-HealthHead)/Default.HealthHead; // Compute total torso damage torsoDamage = 0; if (Default.HealthTorso > 0) torsoDamage = float(Default.HealthTorso-HealthTorso)/Default.HealthTorso; // Compute total health, relative to original health level Health = FClamp(Default.Health - ((limbDamage+headDamage+torsoDamage)*Default.Health), 0.0, Default.Health); } else { // Pawn is invincible - reset health to defaults HealthHead = Default.HealthHead; HealthTorso = Default.HealthTorso; HealthArmLeft = Default.HealthArmLeft; HealthArmRight = Default.HealthArmRight; HealthLegLeft = Default.HealthLegLeft; HealthLegRight = Default.HealthLegRight; Health = Default.Health; } } // ---------------------------------------------------------------------- // UpdatePoison() // ---------------------------------------------------------------------- function UpdatePoison(float deltaTime) { if ((Health <= 0) || bDeleteMe) // no more pain -- you're already dead! return; if (poisonCounter > 0) { poisonTimer += deltaTime; if (poisonTimer >= 2.0) // pain every two seconds { poisonTimer = 0; poisonCounter--; TakeDamage(poisonDamage, Poisoner, Location, vect(0,0,0), 'PoisonEffect'); } if ((poisonCounter <= 0) || (Health <= 0) || bDeleteMe) StopPoison(); } } // ---------------------------------------------------------------------- // StartPoison() // ---------------------------------------------------------------------- function StartPoison(int Damage, Pawn newPoisoner) { if ((Health <= 0) || bDeleteMe) // no more pain -- you're already dead! return; poisonCounter = 8; // take damage no more than eight times (over 16 seconds) poisonTimer = 0; // reset pain timer Poisoner = newPoisoner; if (poisonDamage < Damage) // set damage amount poisonDamage = Damage; } // ---------------------------------------------------------------------- // StopPoison() // ---------------------------------------------------------------------- function StopPoison() { poisonCounter = 0; poisonTimer = 0; poisonDamage = 0; Poisoner = None; } // ---------------------------------------------------------------------- // HasEnemyTimedOut() // ---------------------------------------------------------------------- function bool HasEnemyTimedOut() { if (EnemyTimeout > 0) { if (EnemyLastSeen > EnemyTimeOut && bDefendHome && HomeExtent > 64) {bDefendHome = false; EnemyLastSeen *= 0.3;} //CyberP: yet another hack if (EnemyLastSeen > EnemyTimeout) return true; else return false; } else return false; } // ---------------------------------------------------------------------- // UpdateActorVisibility() // ---------------------------------------------------------------------- function UpdateActorVisibility(actor seeActor, float deltaSeconds, float checkTime, bool bCheckDir) { local bool bCanSee; CheckPeriod += deltaSeconds; if (CheckPeriod >= checkTime) { CheckPeriod = 0.0; if (seeActor != None) bCanSee = (AICanSee(seeActor, ComputeActorVisibility(seeActor), false, bCheckDir, true, true) > 0); else bCanSee = false; if (bCanSee) EnemyLastSeen = 0; else if (EnemyLastSeen <= 0) EnemyLastSeen = 0.01; } if (EnemyLastSeen > 0) EnemyLastSeen += deltaSeconds; } // ---------------------------------------------------------------------- // ComputeActorVisibility() // ---------------------------------------------------------------------- function float ComputeActorVisibility(actor seeActor) { local float visibility; if (seeActor.IsA('DeusExPlayer')) visibility = DeusExPlayer(seeActor).CalculatePlayerVisibility(self); else visibility = 1.0; return (visibility); } // ---------------------------------------------------------------------- // UpdateReactionLevel() [internal use only] // ---------------------------------------------------------------------- function UpdateReactionLevel(bool bRise, float deltaSeconds) { local float surpriseTime; // Handle surprise levels... if (bRise) { if (ReactionLevel < 1.0) { surpriseTime = SurprisePeriod; if (surpriseTime <= 0) surpriseTime = 0.00000001; ReactionLevel += deltaSeconds/surpriseTime; if (ReactionLevel > 1.0) ReactionLevel = 1.0; } } else { if (ReactionLevel > 0.0) { surpriseTime = 7.0; ReactionLevel -= deltaSeconds/surpriseTime; if (ReactionLevel <= 0.0) ReactionLevel = 0.0; } } } // ---------------------------------------------------------------------- // CheckCycle() [internal use only] // ---------------------------------------------------------------------- function Pawn CheckCycle() { local float attackPeriod; local float maxAttackPeriod; local float sustainPeriod; local float decayPeriod; local float minCutoff; local Pawn cycleEnemy; attackPeriod = 0.5; maxAttackPeriod = 4.5; sustainPeriod = 3.0; decayPeriod = 4.0; minCutoff = attackPeriod/maxAttackPeriod; cycleEnemy = None; if (CycleCumulative <= 0) // no enemies seen during this cycle { CycleTimer -= CyclePeriod; if (CycleTimer <= 0) { CycleTimer = 0; EnemyReadiness -= CyclePeriod/decayPeriod; if (EnemyReadiness < 0) EnemyReadiness = 0; } } else // I saw somebody! { CycleTimer = sustainPeriod; CycleCumulative *= 2; // hack if (CycleCumulative < minCutoff) CycleCumulative = minCutoff; else if (CycleCumulative > 1.0) CycleCumulative = 1.0; EnemyReadiness += CycleCumulative*CyclePeriod/attackPeriod; if (EnemyReadiness >= 1.0) { EnemyReadiness = 1.0; if (IsValidEnemy(CycleCandidate)) cycleEnemy = CycleCandidate; } else if (EnemyReadiness >= SightPercentage) if (IsValidEnemy(CycleCandidate)) HandleSighting(CycleCandidate); } CycleCumulative = 0; CyclePeriod = 0; CycleCandidate = None; CycleDistance = 0; return (cycleEnemy); } // ---------------------------------------------------------------------- // CheckEnemyPresence() // ---------------------------------------------------------------------- function bool CheckEnemyPresence(float deltaSeconds, bool bCheckPlayer, bool bCheckOther) { local int i; local int count; local int checked; local Pawn candidate; local float candidateDist; local DeusExPlayer playerCandidate; local bool bCanSee; local int lastCycle; local float visibility; local Pawn cycleEnemy; local bool bValid; local bool bPlayer; local float surpriseTime; local bool bValidEnemy; local bool bPotentialEnemy; local bool bCheck; bValid = false; bCanSee = false; if (bReactPresence && bLookingForEnemy && !bNoNegativeAlliances) { if (PotentialEnemyAlliance != '') bCheck = true; else { for (i=0; i<16; i++) if ((AlliancesEx[i].AllianceLevel < 0) || (AlliancesEx[i].AgitationLevel >= 1.0)) break; if (i < 16) bCheck = true; } if (bCheck) { bValid = true; CyclePeriod += deltaSeconds; count = 0; checked = 0; lastCycle = CycleIndex; foreach CycleActors(Class'Pawn', candidate, CycleIndex) { bValidEnemy = IsValidEnemy(candidate); if (!bValidEnemy && (PotentialEnemyTimer > 0)) if (PotentialEnemyAlliance == candidate.Alliance) bPotentialEnemy = true; if (bValidEnemy || bPotentialEnemy) { count++; bPlayer = candidate.IsA('PlayerPawn'); if ((bPlayer && bCheckPlayer) || (!bPlayer && bCheckOther)) { visibility = AICanSee(candidate, ComputeActorVisibility(candidate), true, true, true, true); if (visibility > 0) { if (bPotentialEnemy) // We can see the potential enemy; ergo, we hate him { IncreaseAgitation(candidate, 1.0); PotentialEnemyAlliance = ''; PotentialEnemyTimer = 0; bValidEnemy = IsValidEnemy(candidate); } if (bValidEnemy) { visibility += VisibilityThreshold; candidateDist = VSize(Location-candidate.Location); if ((CycleCandidate == None) || (CycleDistance > candidateDist)) { CycleCandidate = candidate; CycleDistance = candidateDist; } if (!bPlayer) CycleCumulative += 100000; // a bit of a hack... else CycleCumulative += visibility; } } } if (count >= 1) break; } checked++; if (checked > 20) // hacky hardcoded number break; } if (lastCycle >= CycleIndex) // have we cycled through all actors? { cycleEnemy = CheckCycle(); if (cycleEnemy != None) { SetDistressTimer(); SetEnemy(cycleEnemy, 0, true); bCanSee = true; } } } else bNoNegativeAlliances = True; } // Handle surprise levels... UpdateReactionLevel((EnemyReadiness>0)||(GetStateName()=='Seeking')||bDistressed, deltaSeconds); if (!bValid) { CycleCumulative = 0; CyclePeriod = 0; CycleCandidate = None; CycleDistance = 0; CycleTimer = 0; } return (bCanSee); } // ---------------------------------------------------------------------- // CheckBeamPresence // ---------------------------------------------------------------------- function bool CheckBeamPresence(float deltaSeconds) { local DeusExPlayer player; local Beam beamActor; local bool bReactToBeam; if (bReactPresence && bLookingForEnemy && (BeamCheckTimer <= 0) && (LastRendered() < 5.0)) { BeamCheckTimer = 1.0; player = DeusExPlayer(GetPlayerPawn()); if (player != None) { bReactToBeam = false; if (IsValidEnemy(player)) { foreach RadiusActors(Class'Beam', beamActor, 1200) { if ((beamActor.Owner == player) && (beamActor.LightType != LT_None) && (beamActor.LightBrightness > 32)) { if (VSize(beamActor.Location - Location) < (beamActor.LightRadius+1)*25) bReactToBeam = true; else { if (AICanSee(beamActor, , false, true, false, false) > 0) { if (FastTrace(beamActor.Location, Location+vect(0,0,1)*BaseEyeHeight)) bReactToBeam = true; } } } if (bReactToBeam) break; } } if (bReactToBeam) HandleSighting(player); } } } // ---------------------------------------------------------------------- // CheckCarcassPresence() // ---------------------------------------------------------------------- function bool CheckCarcassPresence(float deltaSeconds) { local Actor carcass; local Name CarcassName; local int lastCycle; local DeusExCarcass body; local DeusExPlayer player; local float visibility; local Name KillerAlliance; local Name killedAlliance; local Pawn killer; local Pawn bestKiller; local float dist; local float bestDist; local float maxCarcassDist; local int maxCarcassCount; if (bFearCarcass && !bHateCarcass && !bReactCarcass) // Major hack! maxCarcassCount = 1; else maxCarcassCount = ArrayCount(Carcasses); //if ((bHateCarcass || bReactCarcass || bFearCarcass) && bLookingForCarcass && (CarcassTimer <= 0)) if ((bHateCarcass || bReactCarcass || bFearCarcass) && (NumCarcasses < maxCarcassCount)) { maxCarcassDist = 1200; if (CarcassCheckTimer <= 0) { CarcassCheckTimer = 0.1; carcass = None; lastCycle = BodyIndex; foreach CycleActors(Class'DeusExCarcass', body, BodyIndex) { if (body.Physics != PHYS_Falling) { if (VSize(body.Location-Location) < maxCarcassDist) { if (GetCarcassData(body, KillerAlliance, killedAlliance, CarcassName, true)) { visibility = AICanSee(body, ComputeActorVisibility(body), true, true, true, true); if (visibility > 0) carcass = body; break; } } } } if (lastCycle >= BodyIndex) { if (carcass == None) { player = DeusExPlayer(GetPlayerPawn()); if (player != None) { if (VSize(player.Location-Location) < maxCarcassDist) { if (GetCarcassData(player, KillerAlliance, killedAlliance, CarcassName, true)) { visibility = AICanSee(player, ComputeActorVisibility(player), true, true, true, true); if (visibility > 0) carcass = player; } } } } } if (carcass != None) { CarcassTimer = 120; AddCarcass(CarcassName); if (bLookingForCarcass) { if (KillerAlliance == 'Player') killer = GetPlayerPawn(); else { bestKiller = None; bestDist = 0; foreach AllActors(Class'Pawn', killer) // hack { if (killer.Alliance == KillerAlliance) { dist = VSize(killer.Location - Location); if ((bestKiller == None) || (bestDist > dist)) { bestKiller = killer; bestDist = dist; } } } killer = bestKiller; } if (bHateCarcass) { PotentialEnemyAlliance = KillerAlliance; PotentialEnemyTimer = 15.0; bNoNegativeAlliances = false; } if (bFearCarcass) IncreaseFear(killer, 2.0); if (bFearCarcass && IsFearful() && !IsValidEnemy(killer)) { SetDistressTimer(); SetEnemy(killer, , true); GotoState('Fleeing'); } else { SetDistressTimer(); SetSeekLocation(killer, carcass.Location, SEEKTYPE_Carcass); HandleEnemy(); } } } } } } // ---------------------------------------------------------------------- // AddProjectileToList() // ---------------------------------------------------------------------- function AddProjectileToList(out NearbyProjectileList projList, DeusExProjectile proj, vector projPos, float dist, float range) { local int count; local int pos; local int bestPos; local float worstDist; bestPos = -1; worstDist = dist; pos = 0; while (pos < ArrayCount(projList.list)) { if (projList.list[pos].projectile == None) { bestPos = pos; break; // short-circuit loop } else { if (worstDist < projList.list[pos].dist) { worstDist = projList.list[pos].dist; bestPos = pos; } } pos++; } if (bestPos >= 0) { projList.list[bestPos].projectile = proj; projList.list[bestPos].location = projPos; projList.list[bestPos].dist = dist; projList.list[bestPos].range = range; } } // ---------------------------------------------------------------------- // IsProjectileDangerous() // ---------------------------------------------------------------------- function bool IsProjectileDangerous(DeusExProjectile projectile) { local bool bEvil; if (projectile.IsA('Cloud')) bEvil = true; else if (projectile.IsA('ThrownProjectile')) { if (projectile.IsA('SpyBot')) bEvil = false; else if ((ThrownProjectile(projectile) != None) && (ThrownProjectile(projectile).bProximityTriggered)) bEvil = false; else bEvil = true; } else bEvil = false; return (bEvil); } // ---------------------------------------------------------------------- // GetProjectileList() // ---------------------------------------------------------------------- function int GetProjectileList(out NearbyProjectileList projList, vector Location) { local float dist; local int count; local DeusExProjectile curProj; local ThrownProjectile throwProj; local Cloud cloudProj; local vector HitNormal, HitLocation; local vector extent; local vector traceEnd; local Actor hitActor; local float range; local vector pos; local float time; local float maxTime; local float elasticity; local int i; local bool bValid; for (i=0; i<ArrayCount(projList.list); i++) projList.list[i].projectile = None; projList.center = Location; maxTime = 2.0; foreach RadiusActors(Class'DeusExProjectile', curProj, 1000) { if (IsProjectileDangerous(curProj)) { throwProj = ThrownProjectile(curProj); cloudProj = Cloud(curProj); extent = vect(1,1,0)*curProj.CollisionRadius; extent.Z = curProj.CollisionHeight; range = VSize(extent); if (curProj.bExplodes) if (range < curProj.blastRadius) range = curProj.blastRadius; if (cloudProj != None) if (range < cloudProj.cloudRadius) range = cloudProj.cloudRadius+128; //CyberP:added+28 range += CollisionRadius+60; if (throwProj != None) elasticity = throwProj.Elasticity; else elasticity = 0.2; bValid = true; if (throwProj != None) if (throwProj.bProximityTriggered) // HACK!!! bValid = false; if (((curProj.Physics == PHYS_Falling) || (curProj.Physics == PHYS_Projectile) || (curProj.Physics == PHYS_None)) && bValid) { pos = curProj.Location; dist = VSize(Location - curProj.Location); AddProjectileToList(projList, curProj, pos, dist, range); if (curProj.Physics == PHYS_Projectile) { traceEnd = curProj.Location + curProj.Velocity*maxTime; hitActor = Trace(HitLocation, HitNormal, traceEnd, curProj.Location, true, extent); if (hitActor == None) pos = traceEnd; else pos = HitLocation; dist = VSize(Location - pos); AddProjectileToList(projList, curProj, pos, dist, range); } else if (curProj.Physics == PHYS_Falling) { time = ParabolicTrace(pos, curProj.Velocity, curProj.Location, true, extent, maxTime, elasticity, curProj.bBounce, 60); if (time > 0) { dist = VSize(Location - pos); AddProjectileToList(projList, curProj, pos, dist, range); } } } } } count = 0; for (i=0; i<ArrayCount(projList.list); i++) if (projList.list[i].projectile != None) count++; return (count); } // ---------------------------------------------------------------------- // IsLocationDangerous() // ---------------------------------------------------------------------- function bool IsLocationDangerous(NearbyProjectileList projList, vector Location) { local bool bDanger; local int i; local float dist; bDanger = false; for (i=0; i<ArrayCount(projList.list); i++) { if (projList.list[i].projectile == None) break; if (projList.center == Location) dist = projList.list[i].dist; else dist = VSize(projList.list[i].location - Location); if (dist < projList.list[i].range) { bDanger = true; break; } } return (bDanger); } // ---------------------------------------------------------------------- // ComputeAwayVector() // ---------------------------------------------------------------------- function vector ComputeAwayVector(NearbyProjectileList projList) { local vector awayVect; local vector tempVect; local rotator tempRot; local int i; local float dist; local int yaw; local int absYaw; local int bestYaw; local NavigationPoint navPoint; local NavigationPoint bestPoint; local float segmentDist; segmentDist = GroundSpeed*0.5; awayVect = vect(0, 0, 0); for (i=0; i<ArrayCount(projList.list); i++) { if ((projList.list[i].projectile != None) && (projList.list[i].dist < projList.list[i].range)) { tempVect = projList.list[i].location - projList.center; if (projList.list[i].dist > 0) tempVect /= projList.list[i].dist; else tempVect = VRand(); awayVect -= tempVect; } } if (awayVect != vect(0, 0, 0)) { awayVect += Normal(Velocity*vect(1,1,0))*0.9; // bias to direction already running yaw = Rotator(awayVect).Yaw; bestPoint = None; foreach ReachablePathnodes(Class'NavigationPoint', navPoint, None, dist) { tempRot = Rotator(navPoint.Location - Location); absYaw = (tempRot.Yaw - Yaw) & 65535; if (absYaw > 32767) absYaw -= 65536; absYaw = Abs(absYaw); if ((bestPoint == None) || (bestYaw > absYaw)) { bestPoint = navPoint; bestYaw = absYaw; } } if (bestPoint != None) awayVect = bestPoint.Location-Location; else { tempRot = Rotator(awayVect); tempRot.Pitch += Rand(7282)-3641; // +/- 20 degrees tempRot.Yaw += Rand(7282)-3641; // +/- 20 degrees awayVect = Vector(tempRot)*segmentDist; } } else awayVect = VRand()*segmentDist; return (awayVect); } // ---------------------------------------------------------------------- // SetupWeapon() // ---------------------------------------------------------------------- function SetupWeapon(bool bDrawWeapon, optional bool bForce) { if (bKeepWeaponDrawn && !bForce) bDrawWeapon = true; if (ShouldDropWeapon()) DropWeapon(); else if (bDrawWeapon) { // if (Weapon == None) SwitchToBestWeapon(); } else SetWeapon(None); } // ---------------------------------------------------------------------- // DropWeapon() // ---------------------------------------------------------------------- function DropWeapon() { local DeusExWeapon dxWeapon; local Weapon oldWeapon; if (Weapon != None && !Weapon.IsA('WeaponRifle')) { dxWeapon = DeusExWeapon(Weapon); if ((dxWeapon == None) || !dxWeapon.bNativeAttack) { oldWeapon = Weapon; SetWeapon(None); if (oldWeapon.IsA('WeaponAssaultGunSpider')) //CyberP: make sure these are destroyed oldWeapon.Destroy(); else oldWeapon.DropFrom(Location); } } } // ---------------------------------------------------------------------- // SetWeapon() // ---------------------------------------------------------------------- function SetWeapon(Weapon newWeapon) { if (Weapon == newWeapon) { if (Weapon != None) { if (Weapon.IsInState('DownWeapon')) Weapon.BringUp(); Weapon.SetDefaultDisplayProperties(); } if (Inventory != None) Inventory.ChangedWeapon(); PendingWeapon = None; return; } PlayWeaponSwitch(newWeapon); if (Weapon != None) { Weapon.SetDefaultDisplayProperties(); Weapon.PutDown(); } Weapon = newWeapon; if (Inventory != None) Inventory.ChangedWeapon(); if (Weapon != None) Weapon.BringUp(); PendingWeapon = None; } // ---------------------------------------------------------------------- // ReactToInjury() // ---------------------------------------------------------------------- function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local Name currentState; local bool bHateThisInjury; local bool bFearThisInjury; if ((health > 0) && (instigatedBy != None) && (bLookingForInjury || bLookingForIndirectInjury)) { currentState = GetStateName(); bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury); bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury); if (bHateThisInjury) IncreaseAgitation(instigatedBy); if (bFearThisInjury) IncreaseFear(instigatedBy, 2.0); if (SetEnemy(instigatedBy)) { SetDistressTimer(); SetNextState('HandlingEnemy'); } else if (bFearThisInjury && IsFearful() ) { SetDistressTimer(); SetEnemy(instigatedBy, , true); SetNextState('Fleeing'); } else { // if (instigatedBy.bIsPlayer) // ReactToFutz(); SetNextState(currentState); } GotoDisabledState(damageType, hitPos); } } // ---------------------------------------------------------------------- // TakeHit() // ---------------------------------------------------------------------- function TakeHit(EHitLocation hitPos) { if (hitPos != HITLOC_None) { PlayTakingHit(hitPos); GotoState('TakingHit'); } else GotoNextState(); } // ---------------------------------------------------------------------- // ComputeFallDirection() // ---------------------------------------------------------------------- function ComputeFallDirection(float totalTime, int numFrames, out vector moveDir, out float stopTime) { // Determine direction, and how long to slide if (AnimSequence == 'DeathFront') { moveDir = Vector(DesiredRotation) * Default.CollisionRadius*0.75; if (numFrames > 5) stopTime = totalTime * ((numFrames-5)/float(numFrames)); else stopTime = totalTime * 0.5; } else if (AnimSequence == 'DeathBack') { moveDir = -Vector(DesiredRotation) * Default.CollisionRadius*0.75; if (numFrames > 5) stopTime = totalTime * ((numFrames-5)/float(numFrames)); else stopTime = totalTime * 0.9; } } // ---------------------------------------------------------------------- // WillTakeStompDamage() // ---------------------------------------------------------------------- function bool WillTakeStompDamage(Actor stomper) { return true; } // ---------------------------------------------------------------------- // DrawShield() // ---------------------------------------------------------------------- function DrawShield() { local EllipseEffect shield; shield = Spawn(class'EllipseEffect', Self,, Location, Rotation); if (shield != None) shield.SetBase(Self); } // ---------------------------------------------------------------------- // StandUp() // ---------------------------------------------------------------------- function StandUp(optional bool bInstant) { local vector placeToStand; if (bSitting) { bSitInterpolation = false; bSitting = false; EnableCollision(true); SetBase(None); SetPhysics(PHYS_Falling); ResetBasedPawnSize(); if (!bInstant && (SeatActor != None) && IsOverlapping(SeatActor)) { bStandInterpolation = true; remainingStandTime = 0.3; StandRate = (Vector(SeatActor.Rotation+Rot(0, -16384, 0))*CollisionRadius) / remainingStandTime; } else StopStanding(); } if (SeatActor != None) { if (SeatActor.sittingActor[seatSlot] == self) SeatActor.sittingActor[seatSlot] = None; SeatActor = None; } if (bDancing) bDancing = false; } // ---------------------------------------------------------------------- // StopStanding() // ---------------------------------------------------------------------- function StopStanding() { if (bStandInterpolation) { bStandInterpolation = false; remainingStandTime = 0; if (Physics == PHYS_Flying) SetPhysics(PHYS_Falling); } } // ---------------------------------------------------------------------- // UpdateStanding() // ---------------------------------------------------------------------- function UpdateStanding(float deltaSeconds) { local float delta; local vector newPos; if (bStandInterpolation) { if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0))) // the bastard's walking now StopStanding(); else { if ((deltaSeconds < remainingStandTime) && (remainingStandTime > 0)) { delta = deltaSeconds; remainingStandTime -= deltaSeconds; } else { delta = remainingStandTime; StopStanding(); } newPos = StandRate*delta; Move(newPos); } } } // ---------------------------------------------------------------------- // JumpOutOfWater() // ---------------------------------------------------------------------- function JumpOutOfWater(vector jumpDir) { Falling(); Velocity = jumpDir * WaterSpeed; Acceleration = jumpDir * AccelRate; velocity.Z = 380; //set here so physics uses this for remainder of tick PlayFalling(); bUpAndOut = true; } // ---------------------------------------------------------------------- // SupportActor() // // Modified from DeusExDecoration.uc // Called when something lands on us // ---------------------------------------------------------------------- function SupportActor(Actor standingActor) { local vector newVelocity; local float angle; local float zVelocity; local float baseMass; local float standingMass; local vector damagePoint; local float damage; standingMass = FMax(1, standingActor.Mass); baseMass = FMax(1, Mass); if ((Physics == PHYS_Swimming) && Region.Zone.bWaterZone) { newVelocity = standingActor.Velocity; newVelocity *= 0.5*standingMass/baseMass; AddVelocity(newVelocity); } else { zVelocity = standingActor.Velocity.Z; damagePoint = Location + vect(0,0,1)*(CollisionHeight-1); damage = (1 - (standingMass/baseMass) * (zVelocity/100)); // Have we been stomped? if ((zVelocity*standingMass < -7500) && (damage > 0) && WillTakeStompDamage(standingActor)) TakeDamage(damage, standingActor.Instigator, damagePoint, 0.2*standingActor.Velocity, 'stomped'); if (self.IsA('Rat')) TakeDamage(6, standingActor.Instigator, damagePoint, 0.2*standingActor.Velocity, 'stomped'); } if (!self.IsA('Rat')) { // Bounce the actor off the pawn angle = FRand()*Pi*2; newVelocity.X = cos(angle); newVelocity.Y = sin(angle); newVelocity.Z = 0; newVelocity *= FRand()*25 + 25; newVelocity += standingActor.Velocity; newVelocity.Z = 50; standingActor.Velocity = newVelocity; standingActor.SetPhysics(PHYS_Falling); } } // ---------------------------------------------------------------------- // PopHead() // ---------------------------------------------------------------------- function PopHead() { } function HelmetBreak() { local int i; local PlasticFragment fragg; local vector loc; loc = Location; loc.Z += CollisionHeight+4; if (mesh != default.mesh) return; for(i=0;i<35;i++) { fragg = Spawn(class'PlasticFragment',self,,loc); if (fragg != None) { fragg.RotationRate = RotRand(); fragg.Skin = Multiskins[6]; fragg.DrawScale *= 0.3 * Frand(); fragg.Velocity = VRand()*80; } } Multiskins[6]=None; bHasHelmet=False; if (IsA('UNATCOTroop')) CarcassType = Class'DeusEx.UNATCOTroopCarcassDehelm'; else if (IsA('Soldier')) CarcassType = Class'DeusEx.SoldierCarcassDehelm'; } singular function HelmetSpawn(Vector hitLocation, int actualDamage, Pawn instigatedBy) { local int i; local GMDXUnatcoDamHelmet dh; local vector loc; loc = Location; loc.Z += CollisionHeight+1; if (mesh != default.mesh || instigatedBy == None) return; dh = Spawn(class'GMDXUnatcoDamHelmet',,,loc); if (dh != None) { dh.Velocity = Vector(instigatedBy.ViewRotation) * (170 + actualDamage); //CyberP/Totalitarian: terrible, lazy hack. dh.Velocity.Z += 50; dh.SetTimer(0.3,false); } Multiskins[6]=None; bHasHelmet=False; if (IsA('UNATCOTroop')) CarcassType = Class'DeusEx.UNATCOTroopCarcassDehelm'; else if (IsA('Soldier')) CarcassType = Class'DeusEx.SoldierCarcassDehelm'; } // ---------------------------------------------------------------------- // SpawnCarcass() // ---------------------------------------------------------------------- function Carcass SpawnCarcass() { local DeusExCarcass carc; local vector loc; local Inventory item, nextItem; local FleshFragment chunk; local BloodSpurt spur; local int i; local float size; // if we really got blown up good, gib us and don't display a carcass if ((Health < -120) && !IsA('Robot')) //CyberP: was -100 { size = (CollisionRadius + CollisionHeight) / 2; //CyberP: was 2 ExpelInventory(); //CyberP: if (size > 10.0) { spawn(class'BoneSkullBloody',,,); spawn(class'FleshFragmentSmoking'); //CyberP: not in the loop below so less spawned. spawn(class'FleshFragmentSmoking'); spawn(class'FleshFragmentSmoking'); //CyberP: not in the loop below so less spawned. spawn(class'FleshFragmentSmoking'); spawn(class'BloodDropFlying'); spawn(class'BloodDropFlying'); spawn(class'BloodDropFlying'); spawn(class'BloodDropFlying'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); if (!IsA('Animal')) Spawn(class'BloodExplodeHit'); loc.X = (1-2*FRand()) * CollisionRadius; loc.Y = (1-2*FRand()) * CollisionRadius; loc.Z = (1-2*FRand()) * CollisionHeight; loc += Location; for (i=0; i<size/1.1; i++) //CyberP: was /1.2 { spawn(class'BloodDropFlying'); chunk = spawn(class'FleshFragment', None,, loc); spur = spawn(class'BloodSpurt'); if (spur!=none && i < 4) { spur.LifeSpan+=0.3; spur.DrawScale+=FRand(); } if (chunk != None) { if (bBurnedUp) { chunk.Velocity.Z = FRand() * 280 + 280; chunk.DrawScale = size / 26; if (FRand() < 0.4) { chunk.Skin=Texture'HDTPDecos.Skins.HDTPAlarmLightTex5';//Texture'Effects.Fire.Wepn_Prifle_SFX'; chunk.LightEffect=LE_FireWaver; chunk.LightBrightness=255; chunk.LightHue=80; chunk.LightSaturation=128; chunk.LightRadius=2; } if (FRand() < 0.3) chunk.bSmoking = true; } else { chunk.Velocity.Z = FRand() * 370 + 370; chunk.DrawScale = size / 22; //Cyberp: was 22 //chunk.SetCollisionSize(chunk.CollisionRadius / chunk.DrawScale, chunk.CollisionHeight / chunk.DrawScale); chunk.bFixedRotationDir = False; chunk.RotationRate = RotRand()*1000; //CyberP: was False } } } } return None; } // spawn the carcass carc = DeusExCarcass(Spawn(CarcassType)); if ( carc != None ) { if (bSetupPop) carc.bPop = True; if (bStunned) carc.bNotDead = True; carc.Initfor(self); // move it down to the floor loc = Location; loc.z -= Default.CollisionHeight; loc.z += carc.Default.CollisionHeight; carc.SetLocation(loc); carc.Velocity = Velocity; carc.Acceleration = Acceleration; // give the carcass the pawn's inventory if we aren't an animal or robot if (!IsA('Animal') && !IsA('Robot')) { if (Inventory != None) { do { item = Inventory; nextItem = item.Inventory; DeleteInventory(item); if ((DeusExWeapon(item) != None) && (DeusExWeapon(item).bNativeAttack)) item.Destroy(); else carc.AddInventory(item); item = nextItem; } until (item == None); } } } return carc; } // ---------------------------------------------------------------------- // ExpelInventory() // // G-Flex: so if the NPC is gibbed, items won't be lost // G-Flex: uses some logic from SpawnCarcass() // ---------------------------------------------------------------------- function ExpelInventory() { local Vector loc; local Inventory item, nextItem; //G-Flex: just like carcass-spawning, don't bother with animal or robot inventories if (!IsA('Animal') && !IsA('Robot')) { if (Inventory != None) { do { item = Inventory; nextItem = item.Inventory; //G-Flex: not necessary to delete items since dropping them does that //DeleteInventory(item); if ((Ammo(item) != None) || ((DeusExWeapon(item) != None) && (DeusExWeapon(item).bNativeAttack || DeusExWeapon(item).IsA('WeaponAssaultGunSpider')))) item.Destroy(); else { //G-Flex: spread them out like chunks, but a little less loc.X = (1-2*FRand()) * CollisionRadius; loc.Y = (1-2*FRand()) * CollisionRadius; loc.Z = (1-2*FRand()) * CollisionHeight; loc += Location; item.DropFrom(loc); item.Velocity += Velocity; item.Velocity += VRand() * (100.0 + (400.0 / item.Mass)); //G-Flex: get the same ammo as from the carcass or a dropped weapon if (item.IsA('DeusExWeapon')) DeusExWeapon(item).SetDroppedAmmoCount(); } item = nextItem; } until (item == None); } } } // ---------------------------------------------------------------------- // FilterDamageType() // ---------------------------------------------------------------------- function bool FilterDamageType(Pawn instigatedBy, Vector hitLocation, Vector offset, Name damageType) { // Special cases for certain damage types if (damageType == 'HalonGas') if (bOnFire) ExtinguishFire(); if (damageType == 'EMP') { //CYBERP: EMP removes cloak outright bHasCloak = False; CloakThreshold = 0; EnableCloak(False); /*CloakEMPTimer += 10; if (CloakEMPTimer > 20) CloakEMPTimer = 20; EnableCloak(bCloakOn); */ return false; } return true; } // ---------------------------------------------------------------------- // ModifyDamage() // ---------------------------------------------------------------------- function float ModifyDamage(int Damage, Pawn instigatedBy, Vector hitLocation, Vector offset, Name damageType) { local int actualDamage; local float headOffsetZ, headOffsetY, armOffset; actualDamage = Damage; // calculate our hit extents headOffsetZ = CollisionHeight * 0.7; headOffsetY = CollisionRadius * 0.3; armOffset = CollisionRadius * 0.35; // if the pawn is stunned, damage is 4X if (bStunned) actualDamage *= 8; //CyberP: override bug: now *8, hacky fix // if the pawn is hit from behind at point-blank range, he is killed instantly else if (offset.x < 0) if ((instigatedBy != None) && (VSize(instigatedBy.Location - Location) < 64)) actualDamage *= 12; //CyberP: was 10 actualDamage = Level.Game.ReduceDamage(actualDamage, DamageType, self, instigatedBy); if (ReducedDamageType == 'All') //God mode actualDamage = 0; else if (Inventory != None) //then check if carrying armor actualDamage = Inventory.ReduceDamage(actualDamage, DamageType, HitLocation); // gas, EMP and nanovirus do no damage if (damageType == 'TearGas' || damageType == 'EMP' || damageType == 'NanoVirus') actualDamage = 0; if (damageType == 'EMP') {bHasCloak = False; CloakThreshold = 0;}//CyberP: EMP just outright disables cloaking. return actualDamage; } // ---------------------------------------------------------------------- // ShieldDamage() // ---------------------------------------------------------------------- function float ShieldDamage(Name damageType) { return 1.0; } // ---------------------------------------------------------------------- // ImpartMomentum() // ---------------------------------------------------------------------- function ImpartMomentum(Vector momentum, Pawn instigatedBy) { if (Physics == PHYS_None) SetMovementPhysics(); if (Physics == PHYS_Walking) momentum.Z = 0.4 * VSize(momentum); if ( instigatedBy == self ) momentum *= 0.6; momentum = momentum/Mass; AddVelocity( momentum ); } // ---------------------------------------------------------------------- // AddVelocity() // ---------------------------------------------------------------------- function AddVelocity(Vector momentum) { if (VSize(momentum) > 0.001) Super.AddVelocity(momentum); } // ---------------------------------------------------------------------- // CanShowPain() // ---------------------------------------------------------------------- function bool CanShowPain() { if (bShowPain && (TakeHitTimer <= 0)) return true; else return false; } // ---------------------------------------------------------------------- // IsPrimaryDamageType() // ---------------------------------------------------------------------- function bool IsPrimaryDamageType(name damageType) { local bool bPrimary; switch (damageType) { case 'Exploded': case 'TearGas': case 'HalonGas': case 'PoisonGas': case 'PoisonEffect': case 'Radiation': case 'EMP': case 'Drowned': case 'NanoVirus': bPrimary = false; break; case 'Stunned': case 'KnockedOut': case 'Burned': case 'Flamed': case 'Poison': case 'Shot': case 'Sabot': default: bPrimary = true; break; } return (bPrimary); } // ---------------------------------------------------------------------- // ShouldReactToInjuryType() // ---------------------------------------------------------------------- function bool ShouldReactToInjuryType(name damageType, bool bHatePrimary, bool bHateSecondary) { local bool bIsPrimary; bIsPrimary = IsPrimaryDamageType(damageType); if ((bHatePrimary && bIsPrimary) || (bHateSecondary && !bIsPrimary)) return true; else return false; } // ---------------------------------------------------------------------- // HandleDamage() // ---------------------------------------------------------------------- function EHitLocation HandleDamage(int actualDamage, Vector hitLocation, Vector offset, name damageType, optional Pawn instigatedBy) { local EHitLocation hitPos; local float headOffsetZ, headOffsetY, armOffset; // calculate our hit extents headOffsetZ = CollisionHeight * 0.7; headOffsetY = CollisionRadius * 0.3; armOffset = CollisionRadius * 0.35; hitPos = HITLOC_None; if (actualDamage > 0) { if (offset.z > headOffsetZ) // head { if (offset.z > 41.0 && bHasHelmet && damageType == 'Shot') //offset.z > 41.0 { PlaySound(sound'ArmorRicochet',SLOT_None,,,1536); if (!IsA('MJ12Troop') && !IsA('MJ12Elite') && !IsA('UNATCOTroop') && (actualDamage >= 25 || FRand() < 0.04)) { HelmetBreak(); } else if (IsA('UNATCOTroop') && (actualDamage >= 25 || FRand() < 0.07)) { HelmetSpawn(hitLocation, actualDamage, instigatedBy); actualDamage = 0; damageType = ''; } else { actualDamage = 0; damageType = ''; } } // narrow the head region if ((Abs(offset.x) < headOffsetY) || (Abs(offset.y) < headOffsetY)) { // don't allow headshots with stunning weapons if ((damageType == 'Stunned') || (damageType == 'KnockedOut')) HealthHead -= actualDamage * 5; //CyberP: Overruled. else HealthHead -= actualDamage * (8+extraMult); //CyberP: gmdx headshot multiplier variation if (offset.x < 0.0) { hitPos = HITLOC_HeadBack; bFlyer=False; // CyberP: DO allow headshots with stunning weapons if from behind. if (((damageType == 'Stunned') || (damageType == 'KnockedOut')) && !IsInState('Attacking')) HealthHead -= actualDamage * 3; //bFrontPop=False; } else { hitPos = HITLOC_HeadFront; bFlyer=False; //bFrontPop=True; } } else // sides of head treated as torso //CyberP: hmm { if ((damageType == 'Stunned') || (damageType == 'KnockedOut')) HealthTorso -= actualDamage * 5; else HealthTorso -= actualDamage * (8+extraMult); //CyberP: lets just multiply the damage as head. if (offset.x < 0.0) { hitPos = HITLOC_TorsoBack; bFlyer=False; // CyberP: DO allow headshots with stunning weapons if from behind. if (((damageType == 'Stunned') || (damageType == 'KnockedOut')) && !IsInState('Attacking')) HealthHead -= actualDamage * 3; } else { hitPos = HITLOC_TorsoFront; bFlyer=False; } } } else if (offset.z < 0.0) // legs { if (offset.y > 0.0) { HealthLegRight -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_RightLegBack; else hitPos = HITLOC_RightLegFront; bFlyer=False; } else { HealthLegLeft -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_LeftLegBack; else hitPos = HITLOC_LeftLegFront; bFlyer=False; } // if this part is already dead, damage the adjacent part if ((HealthLegRight < 0) && (HealthLegLeft > 0)) { HealthLegLeft += HealthLegRight; HealthLegRight = 0; } else if ((HealthLegLeft < 0) && (HealthLegRight > 0)) { HealthLegRight += HealthLegLeft; HealthLegLeft = 0; } if (HealthLegLeft < 0) { HealthTorso += HealthLegLeft; HealthLegLeft = 0; } if (HealthLegRight < 0) { HealthTorso += HealthLegRight; HealthLegRight = 0; } } else // arms and torso { if (offset.y > armOffset) { HealthArmRight -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_RightArmBack; else hitPos = HITLOC_RightArmFront; bFlyer=False; } else if (offset.y < -armOffset) { HealthArmLeft -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_LeftArmBack; else hitPos = HITLOC_LeftArmFront; bFlyer=False; } else { HealthTorso -= actualDamage * 3; //CyberP: was *2. Make torso a bit more vulnerable if (offset.x < 0.0) { hitPos = HITLOC_TorsoBack; bFlyer=False; } else { hitPos = HITLOC_TorsoFront; bFlyer=True; } } // if this part is already dead, damage the adjacent part if (HealthArmLeft < 0) { HealthTorso += HealthArmLeft; HealthArmLeft = 0; } if (HealthArmRight < 0) { HealthTorso += HealthArmRight; HealthArmRight = 0; } } } GenerateTotalHealth(); return hitPos; } // ---------------------------------------------------------------------- // TakeDamageBase() // ---------------------------------------------------------------------- function TakeDamageBase(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType, bool bPlayAnim) { local int actualDamage; local Vector offset; local float origHealth; local EHitLocation hitPos; local float shieldMult; local DeusExPlayer player; //CyberP: for screenflash if near gibs local float dist; //CyberP: for screenflash if near gibs local GMDXImpactSpark AST; local FleshFragmentSmall ffs; local int i; local DeusExWeapon wepa; // use the hitlocation to determine where the pawn is hit // transform the worldspace hitlocation into objectspace // in objectspace, remember X is front to back // Y is side to side, and Z is top to bottom offset = (hitLocation - Location) << Rotation; if (!CanShowPain()) bPlayAnim = false; // Prevent injury if the NPC is intangible if (!bBlockActors && !bBlockPlayers && !bCollideActors) return; // No damage + no damage type = no reaction if ((Damage <= 0) && (damageType == 'None')) return; // Block certain damage types; perform special ops on others if (!FilterDamageType(instigatedBy, hitLocation, offset, damageType)) return; // Impart momentum ImpartMomentum(momentum, instigatedBy); actualDamage = ModifyDamage(Damage, instigatedBy, hitLocation, offset, damageType); if (instigatedBy != None && instigatedBy.IsA('DeusExPlayer')) { if (DeusExPlayer(instigatedBy).bHitmarkerOn) { DeusExPlayer(instigatedBy).hitmarkerTime = 0.2; //DeusExPlayer(instigatedBy).PlaySound(sound'GMDXSFX.Weapons.HitmarkerSFX',SLOT_Interface,,,256); } if (DeusExPlayer(instigatedBy).inHand != None) { if (DeusExPlayer(instigatedBy).inHand.IsA('WeaponNanoSword') || DeusExPlayer(instigatedBy).inHand.IsA('WeaponCrowbar')) extraMult = -2; else if ((DeusExPlayer(instigatedBy).inHand.IsA('DeusExWeapon') && DeusExWeapon(DeusExPlayer(instigatedBy).inHand).ItemName == "USP.10") || DeusExPlayer(instigatedBy).inHand.IsA('WeaponSawedOffShotgun')) extraMult = 1; } } if (actualDamage > 0) { shieldMult = ShieldDamage(damageType); if (shieldMult > 0) actualDamage = Max(int(actualDamage*shieldMult), 1); else actualDamage = 0; if (shieldMult < 1.0) DrawShield(); } origHealth = Health; //log("Damage="$Damage); hitPos = HandleDamage(actualDamage, hitLocation, offset, damageType, instigatedBy); if (!bPlayAnim || (actualDamage <= 0)) hitPos = HITLOC_None; if (bCanBleed) if ((damageType != 'Stunned') && (damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'PoisonGas') && (damageType != 'Radiation') && (damageType != 'EMP') && (damageType != 'NanoVirus') && (damageType != 'Drowned') && (damageType != 'KnockedOut') && (damageType != 'Poison') && (damageType != 'PoisonEffect')) bleedRate += (origHealth-Health)/(0.3*Default.Health); // 1/3 of default health = bleed profusely if (CarriedDecoration != None) DropDecoration(); if ((actualDamage > 0) && (damageType == 'Poison')) StartPoison(Damage, instigatedBy); if (damageType == 'Stunned') { AST=Spawn(class'GMDXImpactSpark'); if (AST != None) { AST.LifeSpan=4.000000; AST.DrawScale=0.000001; AST.Velocity=vect(0,0,0); AST.AmbientSound=Sound'Ambient.Ambient.Electricity3'; AST.SoundVolume=224; AST.SoundRadius=64; AST.SoundPitch=64; } } if (IsA('MIB')) { Die = None; if (damageType == 'Poison' && GroundSpeed>default.GroundSpeed && Fatness != 130) HitSound1 = none; //CyberP: no sound for poisoned augged MiBs if (damageType != 'Poison' && actualDamage > 0 && GroundSpeed>default.GroundSpeed && Fatness != 130) HitSound1 = Sound'DeusExSounds.Generic.ArmorRicochet'; //CyberP: MiB Shield if (Fatness == 130) { if (Health > 450) { Spawn(class'SphereEffectShield'); actualDamage *= 0.3; HitSound1 = none; } else HitSound1 = Sound'DeusExSounds.Generic.ArmorRicochet'; } } if (Health <= 0) { ClearNextState(); //PlayDeathHit(actualDamage, hitLocation, damageType); if ( actualDamage > mass ) Health = -1 * actualDamage; SetEnemy(instigatedBy, 0, true); if (!bSitting && bFlyer && !IsA('Robot') && !IsA('Animal')) { player = DeusExPlayer(GetPlayerPawn()); if (DamageType == 'Shot' && (Damage >= 25 || player.inHand.IsA('WeaponAssaultShotgun') || player.inHand.IsA('WeaponSawedOffShotgun'))) //CyberP: you need to change these conditions { PlaySound(Sound'GMDXSFX.Generic.BloodSpray',SLOT_None,1.5,,1024); SetPhysics(PHYS_Flying); //flying Velocity = (Momentum*0.25)*(25*128); // damage*128 Velocity.Z = 6; //6 bFixedRotationDir = True; if (FRand() < 0.3) DropWeapon(); } } if (HealthHead < 0 && (DamageType == 'Shot' || DamageType == 'Sabot')) { Die = Sound'DeusExSounds.Generic.FleshHit1'; player = DeusExPlayer(GetPlayerPawn()); if (bCanPop && player.bHDTP_ALL == -1 && player.bDecap && (Damage >= 25 || player.inHand.IsA('WeaponAssaultShotgun') || player.inHand.IsA('WeaponSawedOffShotgun'))) //CyberP: you need to change these conditions { bSetupPop = True; spawn(class'BloodSpurt',,,HitLocation); PopHead(); if (player!=none) { dist = Abs(VSize(player.Location - Location)); if (dist < 160) { player.ClientFlash(14, vect(160,0,0)); player.bloodTime = 4.000000; } } for(i=0;i<18;i++) { ffs = spawn(Class'FleshFragmentSmall',,,hitLocation); if (ffs!=none) { ffs.Velocity.Z = FRand() * 200 + 200; ffs.Velocity.X *= 0.85; ffs.Velocity.Y *= 0.85; } spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); } } spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); } // gib us if we get blown up if (DamageType == 'Exploded' || (DamageType == 'Burned' && !bOnFire)) { Die = none; //CyberP: no death sounds if blown up. HitSound1 = none; //CyberP: or pain sounds HitSound2 = none; Health = -10000; if (DamageType == 'Burned') bBurnedUp = True; player = DeusExPlayer(GetPlayerPawn()); //CyberP: for screenflash if near gibs if (player!=none) //GMDX: fix log error { dist = Abs(VSize(player.Location - Location)); if (dist < 256) { player.ClientFlash(14, vect(180,0,0)); player.bloodTime = 5.000000; } } } else { Health = -1; } Died(instigatedBy, damageType, HitLocation); if ((DamageType == 'Flamed') || (DamageType == 'Burned')) { bBurnedToDeath = true; ScaleGlow *= 0.1; // blacken the corpse } else bBurnedToDeath = false; return; } // play a hit sound if (DamageType != 'Stunned') PlayTakeHitSound(actualDamage, damageType, 1); if ((DamageType == 'Flamed') && !bOnFire) CatchFire(); ReactToInjury(instigatedBy, damageType, hitPos); } // ---------------------------------------------------------------------- // IsNearHome() // ---------------------------------------------------------------------- function bool IsNearHome(vector position) { local bool bNear; bNear = true; if (bUseHome) { if (VSize(HomeLoc-position) <= HomeExtent) { if (!FastTrace(position, HomeLoc)) bNear = false; } else bNear = false; } return bNear; } // ---------------------------------------------------------------------- // IsDoor() // ---------------------------------------------------------------------- function bool IsDoor(Actor door, optional bool bWarn) { local bool bIsDoor; local DeusExMover dxMover; bIsDoor = false; dxMover = DeusExMover(door); if (dxMover != None) { if (dxMover.NumKeys > 1) { if (dxMover.bIsDoor) bIsDoor = true; /* else if (bWarn) // hack for now log("WARNING: NPC "$self$" trying to use door "$dxMover$", but bIsDoor flag is False"); */ } } return bIsDoor; } // ---------------------------------------------------------------------- // CheckOpenDoor() // ---------------------------------------------------------------------- function CheckOpenDoor(vector HitNormal, actor Door, optional name Label) { local DeusExMover dxMover; dxMover = DeusExMover(Door); if (dxMover != None) { if (bCanOpenDoors && !IsDoor(dxMover) && dxMover.bBreakable) // break glass we walk into { dxMover.TakeDamage(200, self, dxMover.Location, Velocity, 'Shot'); return; } if (((IsA('MIB') && GroundSpeed > default.GroundSpeed) || IsA('Robot')) && IsDoor(dxMover) && dxMover.bBreakable) //CyberP: super mibs & robots run straight through doors. { dxMover.TakeDamage(200, self, dxMover.Location, Velocity, 'Shot'); return; } if (dxMover.bInterpolating && (dxMover.MoverEncroachType == ME_IgnoreWhenEncroach)) return; if (bCanOpenDoors && bInterruptState && !bInTransientState && IsDoor(dxMover, true)) { if (Label == '') Label = 'Begin'; if (GetStateName() != 'OpeningDoor') SetNextState(GetStateName(), 'ContinueFromDoor'); Target = Door; destLoc = HitNormal; GotoState('OpeningDoor', 'BeginHitNormal'); } else if ((Acceleration != vect(0,0,0)) && (Physics == PHYS_Walking) && (TurnDirection == TURNING_None)) Destination = Location; } } // ---------------------------------------------------------------------- // EncroachedBy() // ---------------------------------------------------------------------- event EncroachedBy( actor Other ) { // overridden so indestructable NPCs aren't InstaGibbed by stupid movement code } // ---------------------------------------------------------------------- // EncroachedByMover() // ---------------------------------------------------------------------- function EncroachedByMover(Mover encroacher) { local DeusExMover dxMover; dxMover = DeusExMover(encroacher); if (dxMover != None) if (!dxMover.bInterpolating && IsDoor(dxMover)) FrobDoor(dxMover); } // ---------------------------------------------------------------------- // FrobDoor() // ---------------------------------------------------------------------- function bool FrobDoor(actor Target) { local DeusExMover dxMover; local DeusExMover triggerMover; local DeusExDecoration trigger; local float dist; local DeusExDecoration bestTrigger; local float bestDist; local bool bDone; bDone = false; dxMover = DeusExMover(Target); if (dxMover != None) { bestTrigger = None; bestDist = 10000; foreach AllActors(Class'DeusExDecoration', trigger) { if (dxMover.Tag == trigger.Event) { dist = VSize(Location - trigger.Location); if ((bestTrigger == None) || (bestDist > dist)) { bestTrigger = trigger; bestDist = dist; } } } if (bestTrigger != None) { foreach AllActors(Class'DeusExMover', triggerMover, dxMover.Tag) triggerMover.Trigger(bestTrigger, self); bDone = true; } else if (dxMover.bFrobbable) { if ((dxMover.WaitingPawn == None) || (dxMover.WaitingPawn == self)) { dxMover.Frob(self, None); bDone = true; } } if (bDone) dxMover.WaitingPawn = self; } return bDone; } // ---------------------------------------------------------------------- // GotoDisabledState() // ---------------------------------------------------------------------- function GotoDisabledState(name damageType, EHitLocation hitPos) { if (!bCollideActors && !bBlockActors && !bBlockPlayers) return; else if ((damageType == 'TearGas') || (damageType == 'HalonGas')) GotoState('RubbingEyes'); else if (damageType == 'Stunned') GotoState('Stunned'); else if (CanShowPain()) TakeHit(hitPos); else GotoNextState(); } // ---------------------------------------------------------------------- // PlayAnimPivot() // ---------------------------------------------------------------------- function PlayAnimPivot(name Sequence, optional float Rate, optional float TweenTime, optional vector NewPrePivot) { if (Rate == 0) Rate = 1.0; if (TweenTime == 0) TweenTime = 0.1; PlayAnim(Sequence, Rate, TweenTime); PrePivotTime = TweenTime; DesiredPrePivot = NewPrePivot + PrePivotOffset; if (PrePivotTime <= 0) PrePivot = DesiredPrePivot; } // ---------------------------------------------------------------------- // LoopAnimPivot() // ---------------------------------------------------------------------- function LoopAnimPivot(name Sequence, optional float Rate, optional float TweenTime, optional float MinRate, optional vector NewPrePivot) { if (Rate == 0) Rate = 1.0; if (TweenTime == 0) TweenTime = 0.1; LoopAnim(Sequence, Rate, TweenTime, MinRate); PrePivotTime = TweenTime; DesiredPrePivot = NewPrePivot + PrePivotOffset; if (PrePivotTime <= 0) PrePivot = DesiredPrePivot; } // ---------------------------------------------------------------------- // TweenAnimPivot() // ---------------------------------------------------------------------- function TweenAnimPivot(name Sequence, float TweenTime, optional vector NewPrePivot) { if (TweenTime == 0) TweenTime = 0.1; TweenAnim(Sequence, TweenTime); PrePivotTime = TweenTime; DesiredPrePivot = NewPrePivot + PrePivotOffset; if (PrePivotTime <= 0) PrePivot = DesiredPrePivot; } // ---------------------------------------------------------------------- // HasTwoHandedWeapon() // ---------------------------------------------------------------------- function Bool HasTwoHandedWeapon() { if ((Weapon != None) && (Weapon.Mass >= 29)) return True; else return False; } // ---------------------------------------------------------------------- // GetStyleTexture() // ---------------------------------------------------------------------- function Texture GetStyleTexture(ERenderStyle newStyle, texture oldTex, optional texture newTex) { local texture defaultTex; if (newStyle == STY_Translucent) defaultTex = Texture'BlackMaskTex'; else if (newStyle == STY_Modulated) defaultTex = Texture'GrayMaskTex'; else if (newStyle == STY_Masked) defaultTex = Texture'PinkMaskTex'; else defaultTex = Texture'BlackMaskTex'; if (oldTex == None) return defaultTex; else if (oldTex == Texture'BlackMaskTex') return Texture'BlackMaskTex'; // hack else if (oldTex == Texture'GrayMaskTex') return defaultTex; else if (oldTex == Texture'PinkMaskTex') return defaultTex; else if (newTex != None) return newTex; else return oldTex; } // ---------------------------------------------------------------------- // SetSkinStyle() // ---------------------------------------------------------------------- function SetSkinStyle(ERenderStyle newStyle, optional texture newTex, optional float newScaleGlow) { local int i; local texture curSkin; local texture oldSkin; if (newScaleGlow == 0) newScaleGlow = ScaleGlow; oldSkin = Skin; for (i=0; i<8; i++) { curSkin = GetMeshTexture(i); MultiSkins[i] = GetStyleTexture(newStyle, curSkin, newTex); } Skin = GetStyleTexture(newStyle, Skin, newTex); ScaleGlow = newScaleGlow; Style = newStyle; } // ---------------------------------------------------------------------- // ResetSkinStyle() // ---------------------------------------------------------------------- function ResetSkinStyle() { local int i; for (i=0; i<8; i++) MultiSkins[i] = Default.MultiSkins[i]; Skin = Default.Skin; ScaleGlow = Default.ScaleGlow; Style = Default.Style; if (IsA('MJ12Troop') && UnfamiliarName == "MJ12 Elite") { MultiSkins[3] = Texture'DeusExCharacters.Skins.MiscTex1'; MultiSkins[6] = Texture'DeusExCharacters.Skins.MJ12TroopTex3'; MultiSkins[7] = None; } } // ---------------------------------------------------------------------- // EnableCloak() // ---------------------------------------------------------------------- function EnableCloak(bool bEnable) // beware! called from C++ { local bool bCloaked; local SpoofedCorona cor; if (!bHasCloak || (CloakEMPTimer > 0) || (Health <= 0) || bOnFire) bEnable = false; if (bEnable && !bCloakOn && !bCloaked) { SetSkinStyle(STY_Translucent, Texture'HDTPAlarmLightTex6', 0.4); SetTimer(0.4,False); cor = Spawn(class'SpoofedCorona'); if (cor != none) cor.SetBase(self); PlaySound(Sound'CloakUp', SLOT_Pain, 0.85, ,768,1.0); AmbientGlow = 255; LightType = LT_Strobe; LightBrightness = 64; LightHue = 160; LightSaturation = 96; LightRadius = 6; KillShadow(); bCloakOn = bEnable; bCloaked = True; } else if (!bEnable && bCloakOn) { ResetSkinStyle(); CreateShadow(); LightRadius = 0; AmbientGlow = 0; bCloakOn = bEnable; bCloaked = False; if (Health > 0) PlaySound(Sound'CloakDown', SLOT_Pain, 0.85, ,768,1.0); } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // SOUND FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // PlayBodyThud() // ---------------------------------------------------------------------- function PlayBodyThud() { PlaySound(sound'BodyThud', SLOT_Interact); } // ---------------------------------------------------------------------- // RandomPitch() // // Repetitive sound pitch randomizer to help make some sounds // sound less monotonous // ---------------------------------------------------------------------- function float RandomPitch() { return (1.1 - 0.2*FRand()); } // ---------------------------------------------------------------------- // Gasp() // ---------------------------------------------------------------------- function Gasp() { PlaySound(sound'MaleGasp', SLOT_Pain,,,, RandomPitch()); } // ---------------------------------------------------------------------- // PlayDyingSound() // ---------------------------------------------------------------------- function PlayDyingSound() { SetDistressTimer(); PlaySound(Die, SLOT_Pain,,,, RandomPitch()); AISendEvent('LoudNoise', EAITYPE_Audio,0.25,490); if (bEmitDistress) AISendEvent('Distress', EAITYPE_Audio,0.25,224); //CyberP: radius was 490 } // ---------------------------------------------------------------------- // PlayIdleSound() // ---------------------------------------------------------------------- function PlayIdleSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_Idle); } // ---------------------------------------------------------------------- // PlayScanningSound() // ---------------------------------------------------------------------- function PlayScanningSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_Scanning); } // ---------------------------------------------------------------------- // PlayPreAttackSearchingSound() // ---------------------------------------------------------------------- function PlayPreAttackSearchingSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) //CyberP: a bit of randomness dxPlayer.StartAIBarkConversation(self, BM_PreAttackSearching); } // ---------------------------------------------------------------------- // PlayPreAttackSightingSound() // ---------------------------------------------------------------------- function PlayPreAttackSightingSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) //CyberP: a bit of randomness dxPlayer.StartAIBarkConversation(self, BM_PreAttackSighting); } // ---------------------------------------------------------------------- // PlayPostAttackSearchingSound() // ---------------------------------------------------------------------- function PlayPostAttackSearchingSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) //CyberP: a bit of randomness dxPlayer.StartAIBarkConversation(self, BM_PostAttackSearching); } // ---------------------------------------------------------------------- // PlayTargetAcquiredSound() // ---------------------------------------------------------------------- function PlayTargetAcquiredSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_TargetAcquired); } // ---------------------------------------------------------------------- // PlayTargetLostSound() // ---------------------------------------------------------------------- function PlayTargetLostSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_TargetLost); } // ---------------------------------------------------------------------- // PlaySearchGiveUpSound() // ---------------------------------------------------------------------- function PlaySearchGiveUpSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) dxPlayer.StartAIBarkConversation(self, BM_SearchGiveUp); } // ---------------------------------------------------------------------- // PlayNewTargetSound() // ---------------------------------------------------------------------- function PlayNewTargetSound() { // someday... } // ---------------------------------------------------------------------- // PlayGoingForAlarmSound() // ---------------------------------------------------------------------- function PlayGoingForAlarmSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer) && (FRand() < 0.7)) dxPlayer.StartAIBarkConversation(self, BM_GoingForAlarm); } // ---------------------------------------------------------------------- // PlayOutOfAmmoSound() // ---------------------------------------------------------------------- function PlayOutOfAmmoSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_OutOfAmmo); } // ---------------------------------------------------------------------- // PlayCriticalDamageSound() // ---------------------------------------------------------------------- function PlayCriticalDamageSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_CriticalDamage); } // ---------------------------------------------------------------------- // PlayAreaSecureSound() // ---------------------------------------------------------------------- function PlayAreaSecureSound() { local DeusExPlayer dxPlayer; // Should we do a player check here? dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer) && (FRand() < 0.7)) //CyberP: cutting down on pawns barking same lines dxPlayer.StartAIBarkConversation(self, BM_AreaSecure); } // ---------------------------------------------------------------------- // PlayFutzSound() // ---------------------------------------------------------------------- function PlayFutzSound() { local DeusExPlayer dxPlayer; local Name conName; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) { if (dxPlayer.barkManager != None) { conName = dxPlayer.barkManager.BuildBarkName(self, BM_Futz); dxPlayer.StartConversationByName(conName, self, !bInterruptState); } // dxPlayer.StartAIBarkConversation(self, BM_Futz); } } // ---------------------------------------------------------------------- // PlayOnFireSound() // ---------------------------------------------------------------------- function PlayOnFireSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_OnFire); } // ---------------------------------------------------------------------- // PlayTearGasSound() // ---------------------------------------------------------------------- function PlayTearGasSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_TearGas); } // ---------------------------------------------------------------------- // PlayCarcassSound() // ---------------------------------------------------------------------- function PlayCarcassSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_Gore); } // ---------------------------------------------------------------------- // PlaySurpriseSound() // ---------------------------------------------------------------------- function PlaySurpriseSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_Surprise); } // ---------------------------------------------------------------------- // PlayAllianceHostileSound() // ---------------------------------------------------------------------- function PlayAllianceHostileSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_AllianceHostile); } // ---------------------------------------------------------------------- // PlayAllianceFriendlySound() // ---------------------------------------------------------------------- function PlayAllianceFriendlySound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_AllianceFriendly); } // ---------------------------------------------------------------------- // PlayTakeHitSound() // ---------------------------------------------------------------------- function PlayTakeHitSound(int Damage, name damageType, int Mult) { local Sound hitSound; local float volume; if (Level.TimeSeconds - LastPainSound < 0.33) //CyberP: modded return; if (Damage <= 0) return; LastPainSound = Level.TimeSeconds; if (FRand() < 0.15) hitSound = HitSound2; else hitSound = HitSound1; //CyberP: more lax conditions for hitsound2 volume = FMax(Mult*TransientSoundVolume, Mult*2.0); SetDistressTimer(); if (damageType != 'Exploded') PlaySound(hitSound, SLOT_Pain, volume,,, RandomPitch()); if ((hitSound != None) && bEmitDistress) AISendEvent('Distress', EAITYPE_Audio, volume*0.75); //CyberP: *0.75 added } // ---------------------------------------------------------------------- // GetFloorMaterial() // // Gets the name of the texture group that we are standing on // ---------------------------------------------------------------------- function name GetFloorMaterial() { local vector EndTrace, HitLocation, HitNormal; local actor target; local int texFlags; local name texName, texGroup; // trace down to our feet EndTrace = Location - CollisionHeight * 2 * vect(0,0,1); foreach TraceTexture(class'Actor', target, texName, texGroup, texFlags, HitLocation, HitNormal, EndTrace) { if ((target == Level) || target.IsA('Mover')) break; } return texGroup; } // ---------------------------------------------------------------------- // PlayFootStep() // // Plays footstep sounds based on the texture group // (yes, I know this looks nasty -- I'll have to figure out a cleaner way to do this) // ---------------------------------------------------------------------- function PlayFootStep() { local Sound stepSound; local float rnd; local name mat; local float speedFactor, massFactor; local float volume, pitch, range; local float radius, maxRadius; local float volumeMultiplier; local DeusExPlayer dxPlayer; local float shakeRadius, shakeMagnitude; local float playerDist; rnd = FRand(); mat = GetFloorMaterial(); volumeMultiplier = 1.0; if (WalkSound == None) { if (FootRegion.Zone.bWaterZone) { if (rnd < 0.33) stepSound = Sound'WaterStep1'; else if (rnd < 0.66) stepSound = Sound'WaterStep2'; else stepSound = Sound'WaterStep3'; } else { switch(mat) { case 'Textile': case 'Paper': volumeMultiplier = 0.6; if (rnd < 0.25) stepSound = Sound'CarpetStep1'; else if (rnd < 0.5) stepSound = Sound'CarpetStep2'; else if (rnd < 0.75) stepSound = Sound'CarpetStep3'; else stepSound = Sound'CarpetStep4'; break; case 'Earth': volumeMultiplier = 0.8; if (rnd < 0.25) stepSound = Sound'DIRT1'; else if (rnd < 0.5) stepSound = Sound'DIRT2'; else if (rnd < 0.75) stepSound = Sound'DIRT3'; else stepSound = Sound'DIRT4'; break; case 'Foliage': volumeMultiplier = 0.7; if (rnd < 0.25) stepSound = Sound'GrassStep1'; else if (rnd < 0.5) stepSound = Sound'GrassStep2'; else if (rnd < 0.75) stepSound = Sound'GrassStep3'; else stepSound = Sound'GrassStep4'; break; case 'Metal': volumeMultiplier = 0.9; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer.bThisMission && dxPlayer != none) { if (rnd < 0.25) stepSound = Sound'GRATE1'; else if (rnd < 0.5) stepSound = Sound'GRATE2'; else if (rnd < 0.75) stepSound = Sound'GRATE3'; else stepSound = Sound'GRATE4'; } else { if (rnd < 0.25) stepSound = Sound'MetalStep1'; else if (rnd < 0.5) stepSound = Sound'MetalStep2'; else if (rnd < 0.75) stepSound = Sound'MetalStep3'; else stepSound = Sound'MetalStep4'; } break; case 'Ladder': volumeMultiplier = 1.0; if (rnd < 0.25) stepSound = Sound'GRATE1'; else if (rnd < 0.5) stepSound = Sound'GRATE2'; else if (rnd < 0.75) stepSound = Sound'GRATE3'; else stepSound = Sound'GRATE4'; break; case 'Glass': volumeMultiplier = 0.7; if (rnd < 0.25) stepSound = Sound'GLASS1'; else if (rnd < 0.5) stepSound = Sound'GLASS2'; else if (rnd < 0.75) stepSound = Sound'GLASS3'; else stepSound = Sound'GLASS4'; break; case 'Ceramic': case 'Tiles': volumeMultiplier = 0.75; if (rnd < 0.25) stepSound = Sound'TileStep1'; else if (rnd < 0.5) stepSound = Sound'TileStep2'; else if (rnd < 0.75) stepSound = Sound'TileStep3'; else stepSound = Sound'TileStep4'; break; case 'Wood': volumeMultiplier = 0.8; if (rnd < 0.25) stepSound = Sound'WoodStep1'; else if (rnd < 0.5) stepSound = Sound'WoodStep2'; else if (rnd < 0.75) stepSound = Sound'WoodStep3'; else stepSound = Sound'WoodStep4'; break; case 'Stucco': volumeMultiplier = 0.7; if (rnd < 0.25) stepSound = Sound'CARDB1'; else if (rnd < 0.5) stepSound = Sound'CARDB2'; else if (rnd < 0.75) stepSound = Sound'CARDB3'; else stepSound = Sound'CARDB4'; break; case 'Brick': case 'Concrete': volumeMultiplier = 0.95; if (rnd < 0.25) stepSound = Sound'STEP1'; else if (rnd < 0.5) stepSound = Sound'STEP2'; else if (rnd < 0.75) stepSound = Sound'STEP3'; else stepSound = Sound'STEP4'; break; case 'Stone': default: volumeMultiplier = 0.8; if (rnd < 0.25) stepSound = Sound'StoneStep1'; else if (rnd < 0.5) stepSound = Sound'StoneStep2'; else if (rnd < 0.75) stepSound = Sound'StoneStep3'; else stepSound = Sound'StoneStep4'; break; } } } else stepSound = WalkSound; // compute sound volume, range and pitch, based on mass and speed speedFactor = VSize(Velocity)/120.0; massFactor = Mass/150.0; radius = 768.0; maxRadius = 2048.0; // volume = (speedFactor+0.2)*massFactor; // volume = (speedFactor+0.7)*massFactor; volume = massFactor*1.5; range = radius * volume; pitch = (volume+0.5); volume = 1.0; range = FClamp(range, 0.01, maxRadius); pitch = FClamp(pitch, 1.0, 1.5); // play the sound and send an AI event PlaySound(stepSound, SLOT_Interact, volume, , range, pitch); AISendEvent('LoudNoise', EAITYPE_Audio, volume*volumeMultiplier, range*volumeMultiplier); // Shake the camera when heavy things tread if (Mass > 400) { radius = 1536.0; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) { playerDist = DistanceFromPlayer; shakeRadius = FClamp((Mass-400)/300, 0, 1.3) * (range*0.6); //CyberP: modded all this to make milbots more scary shakeMagnitude = FClamp((Mass-400)/1600, 0, 1.3); shakeMagnitude = FClamp(1.0-(playerDist/shakeRadius), 0, 1.3) * shakeMagnitude; if (shakeMagnitude > 0) dxPlayer.JoltView(shakeMagnitude); } } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // ANIMATION CALLS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // GetSwimPivot() // ---------------------------------------------------------------------- function vector GetSwimPivot() { // THIS IS A HIDEOUS, UGLY, MASSIVELY EVIL HACK!!!! return (vect(0,0,1)*CollisionHeight*0.65); } // ---------------------------------------------------------------------- // GetWalkingSpeed() // ---------------------------------------------------------------------- function float GetWalkingSpeed() { if (Physics == PHYS_Swimming) return MaxDesiredSpeed; else return WalkingSpeed; } // ---------------------------------------------------------------------- // PlayTurnHead() // ---------------------------------------------------------------------- function bool PlayTurnHead(ELookDirection newLookDir, float rate, float tweentime) { if (bCanTurnHead) { if (Super.PlayTurnHead(newLookDir, rate, tweentime)) { AIAddViewRotation = rot(0,0,0); // default switch (newLookDir) { case LOOK_Left: AIAddViewRotation = rot(0,-5461,0); // 30 degrees left break; case LOOK_Right: AIAddViewRotation = rot(0,5461,0); // 30 degrees right break; case LOOK_Up: AIAddViewRotation = rot(5461,0,0); // 30 degrees up break; case LOOK_Down: AIAddViewRotation = rot(-5461,0,0); // 30 degrees down break; case LOOK_Forward: AIAddViewRotation = rot(0,0,0); // 0 degrees break; } } else return false; } else return false; } // ---------------------------------------------------------------------- // PlayRunningAndFiring() // ---------------------------------------------------------------------- function PlayRunningAndFiring() { local DeusExWeapon W; local vector v1, v2; local float dotp; bIsWalking = FALSE; W = DeusExWeapon(Weapon); if (W != None) { if (Region.Zone.bWaterZone) { if (W.bHandToHand) LoopAnimPivot('Tread',,0.1,,GetSwimPivot()); else LoopAnimPivot('TreadShoot',,0.1,,GetSwimPivot()); } else { if (W.bHandToHand) LoopAnimPivot('Run',runAnimMult,0.1); else { v1 = Normal((Enemy.Location - Location)*vect(1,1,0)); if (destPoint != None) v2 = Normal((destPoint.Location - Location)*vect(1,1,0)); else v2 = Normal((destLoc - Location)*vect(1,1,0)); dotp = Abs(v1 dot v2); if (dotp < 0.70710678) // running sideways { if (HasTwoHandedWeapon()) LoopAnimPivot('Strafe2H',runAnimMult,0.1); else LoopAnimPivot('Strafe',runAnimMult,0.1); } else { if (HasTwoHandedWeapon()) LoopAnimPivot('RunShoot2H',runAnimMult,0.1); else LoopAnimPivot('RunShoot',runAnimMult,0.1); } } } } } // ---------------------------------------------------------------------- // PlayReloadBegin() // ---------------------------------------------------------------------- function PlayReloadBegin() { PlayAnimPivot('ReloadBegin',, 0.1); } // ---------------------------------------------------------------------- // PlayReload() // ---------------------------------------------------------------------- function PlayReload() { LoopAnimPivot('Reload',,0.2); } // ---------------------------------------------------------------------- // PlayReloadEnd() // ---------------------------------------------------------------------- function PlayReloadEnd() { PlayAnimPivot('ReloadEnd',, 0.1); } // ---------------------------------------------------------------------- // TweenToShoot() // ---------------------------------------------------------------------- function TweenToShoot(float tweentime) { if (Region.Zone.bWaterZone) TweenAnimPivot('TreadShoot', tweentime, GetSwimPivot()); else if (!bCrouching) { if (!IsWeaponReloading()) { if (HasTwoHandedWeapon()) TweenAnimPivot('Shoot2H', tweentime); else TweenAnimPivot('Shoot', tweentime); } else PlayReload(); } } // ---------------------------------------------------------------------- // PlayShoot() // ---------------------------------------------------------------------- function PlayShoot() { if (Region.Zone.bWaterZone) PlayAnimPivot('TreadShoot', , 0, GetSwimPivot()); else { if (HasTwoHandedWeapon()) PlayAnimPivot('Shoot2H', , 0); else PlayAnimPivot('Shoot', , 0); } } // ---------------------------------------------------------------------- // TweenToCrouchShoot() // ---------------------------------------------------------------------- function TweenToCrouchShoot(float tweentime) { if (Region.Zone.bWaterZone) TweenAnimPivot('TreadShoot', tweentime, GetSwimPivot()); else TweenAnimPivot('CrouchShoot', tweentime); } // ---------------------------------------------------------------------- // PlayCrouchShoot() // ---------------------------------------------------------------------- function PlayCrouchShoot() { if (Region.Zone.bWaterZone) PlayAnimPivot('TreadShoot', , 0, GetSwimPivot()); else PlayAnimPivot('CrouchShoot', , 0); } // ---------------------------------------------------------------------- // TweenToAttack() // ---------------------------------------------------------------------- function TweenToAttack(float tweentime) { if (Region.Zone.bWaterZone) TweenAnimPivot('Tread', tweentime, GetSwimPivot()); else { if (bUseSecondaryAttack) TweenAnimPivot('AttackSide', tweentime); else TweenAnimPivot('Attack', tweentime); } } // ---------------------------------------------------------------------- // PlayAttack() // ---------------------------------------------------------------------- function PlayAttack() { local DeusExWeapon W; W = DeusExWeapon(Weapon); if (Region.Zone.bWaterZone) PlayAnimPivot('Tread',,,GetSwimPivot()); else { if (bUseSecondaryAttack) PlayAnimPivot('AttackSide',1.25,0.1); //CyberP: added 1.2, same as below else if (W != None && W.bHandToHand) PlayAnimPivot('Attack',1.2,0.1); //CyberP: faster attack rate with melee else PlayAnimPivot('Attack'); } } // ---------------------------------------------------------------------- // PlayTurning() // ---------------------------------------------------------------------- function PlayTurning() { // ClientMessage("PlayTurning()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , , , GetSwimPivot()); else { if (HasTwoHandedWeapon()) TweenAnimPivot('Walk2H', 0.1); else TweenAnimPivot('Walk', 0.1); } } // ---------------------------------------------------------------------- // TweenToWalking() // ---------------------------------------------------------------------- function TweenToWalking(float tweentime) { // ClientMessage("TweenToWalking()"); bIsWalking = True; if (Region.Zone.bWaterZone) TweenAnimPivot('Tread', tweentime, GetSwimPivot()); else { if (HasTwoHandedWeapon()) TweenAnimPivot('Walk2H', tweentime); else TweenAnimPivot('Walk', tweentime); } } // ---------------------------------------------------------------------- // PlayWalking() // ---------------------------------------------------------------------- function PlayWalking() { // ClientMessage("PlayWalking()"); bIsWalking = True; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.15, , GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('Walk2H',walkAnimMult, 0.15); else LoopAnimPivot('Walk',walkAnimMult, 0.15); } } // ---------------------------------------------------------------------- // TweenToRunning() // ---------------------------------------------------------------------- function TweenToRunning(float tweentime) { // ClientMessage("TweenToRunning()"); bIsWalking = False; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',, tweentime,, GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('RunShoot2H', runAnimMult, tweentime); else LoopAnimPivot('Run', runAnimMult, tweentime); } } // ---------------------------------------------------------------------- // PlayRunning() // ---------------------------------------------------------------------- function PlayRunning() { // ClientMessage("PlayRunning()"); bIsWalking = False; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('RunShoot2H', runAnimMult); else LoopAnimPivot('Run', runAnimMult); } } // ---------------------------------------------------------------------- // PlayPanicRunning() // ---------------------------------------------------------------------- function PlayPanicRunning() { // ClientMessage("PlayPanicRunning()"); bIsWalking = False; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else LoopAnimPivot('Panic', runAnimMult); } // ---------------------------------------------------------------------- // TweenToWaiting() // ---------------------------------------------------------------------- function TweenToWaiting(float tweentime) { // ClientMessage("TweenToWaiting()"); if (Region.Zone.bWaterZone) TweenAnimPivot('Tread', tweentime, GetSwimPivot()); else { if (HasTwoHandedWeapon()) TweenAnimPivot('BreatheLight2H', tweentime); else TweenAnimPivot('BreatheLight', tweentime); } } // ---------------------------------------------------------------------- // PlayWaiting() // ---------------------------------------------------------------------- function PlayWaiting() { // ClientMessage("PlayWaiting()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.3, , GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('BreatheLight2H', , 0.3); else LoopAnimPivot('BreatheLight', , 0.3); } } // ---------------------------------------------------------------------- // PlayIdle() // ---------------------------------------------------------------------- function PlayIdle() { // ClientMessage("PlayIdle()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.3, , GetSwimPivot()); else { if (HasTwoHandedWeapon()) PlayAnimPivot('Idle12H', , 0.3); else PlayAnimPivot('Idle1', , 0.3); } } // ---------------------------------------------------------------------- // PlayDancing() // ---------------------------------------------------------------------- function PlayDancing() { // ClientMessage("PlayDancing()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.3, , GetSwimPivot()); else LoopAnimPivot('Dance', FRand()*0.2+0.9, 0.3); } // ---------------------------------------------------------------------- // PlaySittingDown() // ---------------------------------------------------------------------- function PlaySittingDown() { // ClientMessage("PlaySittingDown()"); PlayAnimPivot('SitBegin', , 0.15); } // ---------------------------------------------------------------------- // PlaySitting() // ---------------------------------------------------------------------- function PlaySitting() { // ClientMessage("PlaySitting()"); LoopAnimPivot('SitBreathe', , 0.15); } // ---------------------------------------------------------------------- // PlayStandingUp() // ---------------------------------------------------------------------- function PlayStandingUp() { // ClientMessage("PlayStandingUp()"); PlayAnimPivot('SitStand', , 0.15); } // ---------------------------------------------------------------------- // PlayRubbingEyesStart() // ---------------------------------------------------------------------- function PlayRubbingEyesStart() { // ClientMessage("PlayRubbingEyesStart()"); PlayAnimPivot('RubEyesStart', , 0.15); } // ---------------------------------------------------------------------- // PlayRubbingEyes() // ---------------------------------------------------------------------- function PlayRubbingEyes() { // ClientMessage("PlayRubbingEyes()"); LoopAnimPivot('RubEyes'); } // ---------------------------------------------------------------------- // PlayRubbingEyesEnd() // ---------------------------------------------------------------------- function PlayRubbingEyesEnd() { // ClientMessage("PlayRubbingEyesEnd()"); PlayAnimPivot('RubEyesStop'); } // ---------------------------------------------------------------------- // PlayCowerBegin() // ---------------------------------------------------------------------- function PlayCowerBegin() { // ClientMessage("PlayCowerBegin()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else PlayAnimPivot('CowerBegin'); } // ---------------------------------------------------------------------- // PlayCowering() // ---------------------------------------------------------------------- function PlayCowering() { // ClientMessage("PlayCowering()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else LoopAnimPivot('CowerStill'); } // ---------------------------------------------------------------------- // PlayCowerEnd() // ---------------------------------------------------------------------- function PlayCowerEnd() { // ClientMessage("PlayCowerEnd()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else PlayAnimPivot('CowerEnd'); } // ---------------------------------------------------------------------- // PlayStunned() // ---------------------------------------------------------------------- function PlayStunned() { // ClientMessage("PlayStunned()"); LoopAnimPivot('Shocked'); } // ---------------------------------------------------------------------- // TweenToSwimming() // ---------------------------------------------------------------------- function TweenToSwimming(float tweentime) { // ClientMessage("TweenToSwimming()"); TweenAnimPivot('Tread', tweentime, GetSwimPivot()); } // ---------------------------------------------------------------------- // PlaySwimming() // ---------------------------------------------------------------------- function PlaySwimming() { // ClientMessage("PlaySwimming()"); LoopAnimPivot('Tread', , , , GetSwimPivot()); } // ---------------------------------------------------------------------- // PlayFalling() // ---------------------------------------------------------------------- function PlayFalling() { // ClientMessage("PlayFalling()"); PlayAnimPivot('Jump', 3, 0.1); } // ---------------------------------------------------------------------- // PlayLanded() // ---------------------------------------------------------------------- function PlayLanded(float impactVel) { // ClientMessage("PlayLanded()"); bIsWalking = True; if (impactVel < -12*CollisionHeight) PlayAnimPivot('Land'); } // ---------------------------------------------------------------------- // PlayDuck() // ---------------------------------------------------------------------- function PlayDuck() { // ClientMessage("PlayDuck()"); TweenAnimPivot('CrouchWalk', 0.25); // PlayAnimPivot('Crouch'); } // ---------------------------------------------------------------------- // PlayRising() // ---------------------------------------------------------------------- function PlayRising() { // ClientMessage("PlayRising()"); PlayAnimPivot('Stand'); } // ---------------------------------------------------------------------- // PlayCrawling() // ---------------------------------------------------------------------- function PlayCrawling() { // ClientMessage("PlayCrawling()"); LoopAnimPivot('CrouchWalk'); } // ---------------------------------------------------------------------- // PlayPushing() // ---------------------------------------------------------------------- function PlayPushing() { // ClientMessage("PlayPushing()"); PlayAnimPivot('PushButton',1.2 , 0.15); //CyberP: added 1.2 rate } // ---------------------------------------------------------------------- // PlayBeginAttack() // ---------------------------------------------------------------------- function bool PlayBeginAttack() { return false; } // ---------------------------------------------------------------------- // PlayFiring() // ---------------------------------------------------------------------- /* function PlayFiring() { local DeusExWeapon W; // ClientMessage("PlayFiring()"); W = DeusExWeapon(Weapon); if (W != None) { if (W.bHandToHand) { PlayAnimPivot('Attack',1.1,0.1); } else { if (W.bAutomatic) { if (HasTwoHandedWeapon()) LoopAnimPivot('Shoot2H',,0.1); else LoopAnimPivot('Shoot',,0.1); } else { if (HasTwoHandedWeapon()) PlayAnimPivot('Shoot2H',,0.1); else PlayAnimPivot('Shoot',,0.1); } } } } */ // ---------------------------------------------------------------------- // PlayTakingHit() // ---------------------------------------------------------------------- function PlayTakingHit(EHitLocation hitPos) { local vector pivot; local name animName; animName = ''; if (!Region.Zone.bWaterZone) { switch (hitPos) { case HITLOC_HeadFront: animName = 'HitHead'; break; case HITLOC_TorsoFront: animName = 'HitTorso'; break; case HITLOC_LeftArmFront: animName = 'HitArmLeft'; break; case HITLOC_RightArmFront: animName = 'HitArmRight'; break; case HITLOC_HeadBack: animName = 'HitHeadBack'; break; case HITLOC_TorsoBack: case HITLOC_LeftArmBack: case HITLOC_RightArmBack: animName = 'HitTorsoBack'; break; case HITLOC_LeftLegFront: case HITLOC_LeftLegBack: animName = 'HitLegLeft'; break; case HITLOC_RightLegFront: case HITLOC_RightLegBack: animName = 'HitLegRight'; break; } pivot = vect(0,0,0); } else { switch (hitPos) { case HITLOC_HeadFront: case HITLOC_TorsoFront: case HITLOC_LeftLegFront: case HITLOC_RightLegFront: case HITLOC_LeftArmFront: case HITLOC_RightArmFront: animName = 'WaterHitTorso'; break; case HITLOC_HeadBack: case HITLOC_TorsoBack: case HITLOC_LeftLegBack: case HITLOC_RightLegBack: case HITLOC_LeftArmBack: case HITLOC_RightArmBack: animName = 'WaterHitTorsoBack'; break; } pivot = GetSwimPivot(); } if (animName != '') PlayAnimPivot(animName,0.9 , 0.1, pivot); //CyberP: 0.9 added } // ---------------------------------------------------------------------- // PlayWeaponSwitch() // ---------------------------------------------------------------------- function PlayWeaponSwitch(Weapon newWeapon) { // ClientMessage("PlayWeaponSwitch()"); } // ---------------------------------------------------------------------- // PlayDying() // ---------------------------------------------------------------------- function PlayDying(name damageType, vector hitLoc) { local Vector X, Y, Z; local float dotp; local float multa; // ClientMessage("PlayDying()"); if (bFlyer && Physics == PHYS_Flying) multa= 1.45; else multa= 1; if (Region.Zone.bWaterZone) PlayAnimPivot('WaterDeath',, 0.1); else if (bSitting) // if sitting, always fall forward PlayAnimPivot('DeathFront',1.1, 0.1); //CyberP: else if (AnimSequence == 'PushButton' ) PlayAnimPivot('DeathBack',1.05*multa, 0.1); else { GetAxes(Rotation, X, Y, Z); dotp = (Location - HitLoc) dot X; // die from the correct side if (dotp < 0.0) // shot from the front, fall back PlayAnimPivot('DeathBack',1.05*multa, 0.1); //cyberP: sped up death back else // shot from the back, fall front PlayAnimPivot('DeathFront',1.05*multa, 0.1); //CyberP: sped up death front } // don't scream if we are stunned if ((damageType == 'Stunned') || (damageType == 'KnockedOut') || (damageType == 'Poison') || (damageType == 'PoisonEffect')) { bStunned = True; if (bIsFemale) //CyberP: lets add a bit of variety to K.O sounds. { if (FRand() < 0.5) PlaySound(Sound'FemaleUnconscious', SLOT_Pain,,,, RandomPitch()); else { } } else if (IsA('MJ12Commando')) PlaySound(Sound'CloakDown', SLOT_Pain,,,, RandomPitch()); else if ((IsA('HKMilitary') || IsA('TriadLumPath')) && (FRand() < 0.7)) PlaySound(Sound'Death07', SLOT_Pain); else if (FRand() < 0.25) PlaySound(Sound'Death09', SLOT_Pain); else if (FRand() < 0.4) { } else PlaySound(Sound'MaleUnconscious', SLOT_Pain,,,, RandomPitch()); } else { bStunned = False; PlayDyingSound(); } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // FIRE ROUTINES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CatchFire() // ---------------------------------------------------------------------- //ok, this is going to be completely replaced with a system devised by smoke39, who also //discovered the renderoverlays trick. Which is nice. -DDL function CatchFire() { local Fire f; local int i; local vector loc; if (bOnFire || Region.Zone.bWaterZone || (BurnPeriod <= 0) || bInvincible) return; bOnFire = True; burnTimer = 0; EnableCloak(false); // for (i=0; i<8; i++) // { // loc.X = 0.5*CollisionRadius * (1.0-2.0*FRand()); // loc.Y = 0.5*CollisionRadius * (1.0-2.0*FRand()); // loc.Z = 0.6*CollisionHeight * (1.0-2.0*FRand()); // loc += Location; // f = Spawn(class'Fire', Self,, loc); // if (f != None) // { // f.DrawScale = 0.5*FRand() + 1.0; // // // turn off the sound and lights for all but the first one // if (i > 0) // { // f.AmbientSound = None; // f.LightType = LT_None; // } // // // turn on/off extra fire and smoke // if (FRand() < 0.5) // f.smokeGen.Destroy(); // if (FRand() < 0.5) // f.AddFire(); // } // } //smoke39's system Spawn(class'FireMesh', self); // set the burn timer SetTimer(1.0, True); } // ---------------------------------------------------------------------- // ExtinguishFire() // ---------------------------------------------------------------------- function ExtinguishFire() { local FireMesh f; bOnFire = False; burnTimer = 0; SetTimer(0, False); // foreach BasedActors(class'Fire', f) // f.Destroy(); foreach BasedActors(class'FireMesh', f) { // Smoke39 - don't destroy it, just make it smoke // f.Destroy(); if(Health <= 0) f.bDying = true; f.Smoke(); } } // ---------------------------------------------------------------------- // UpdateFire() // ---------------------------------------------------------------------- function UpdateFire() { // continually burn and do damage HealthTorso -= 10; if (IsA('MIB')) HealthTorso -= 20; GenerateTotalHealth(); if (Health <= 0) { TakeDamage(20, None, Location, vect(0,0,0), 'Burned'); ExtinguishFire(); } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CONVERSATION FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CanConverse() // ---------------------------------------------------------------------- function bool CanConverse() { // Return True if this NPC is in a conversable state return (bCanConverse && bInterruptState && ((Physics == PHYS_Walking) || (Physics == PHYS_Flying))); } // ---------------------------------------------------------------------- // CanConverseWithPlayer() // ---------------------------------------------------------------------- function bool CanConverseWithPlayer(DeusExPlayer dxPlayer) { local name alliance1, alliance2, carcname; // temp vars if (GetPawnAllianceType(dxPlayer) == ALLIANCE_Hostile) return false; else if ((GetStateName() == 'Fleeing') && (Enemy != dxPlayer) && (IsValidEnemy(Enemy, false))) // hack return false; else if (GetCarcassData(dxPlayer, alliance1, alliance2, carcname)) return false; else return true; } // ---------------------------------------------------------------------- // EndConversation() // ---------------------------------------------------------------------- function EndConversation() { Super.EndConversation(); if ((GetStateName() == 'Conversation') || (GetStateName() == 'FirstPersonConversation')) { bConversationEndedNormally = True; if (!bConvEndState) FollowOrders(); } bInConversation = False; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // STIMULI AND AGITATION ROUTINES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // LoudNoiseScore() // ---------------------------------------------------------------------- function float LoudNoiseScore(actor receiver, actor sender, float score) { local Pawn pawnSender; // Cull events received from friends pawnSender = Pawn(sender); if (pawnSender == None) pawnSender = sender.Instigator; if (pawnSender == None) score = 0; else if (!IsValidEnemy(pawnSender)) score = 0; return score; } // ---------------------------------------------------------------------- // WeaponDrawnScore() // ---------------------------------------------------------------------- function float WeaponDrawnScore(actor receiver, actor sender, float score) { local Pawn pawnSender; // Cull events received from enemies pawnSender = Pawn(sender); if (pawnSender == None) pawnSender = Pawn(sender.Owner); if (pawnSender == None) pawnSender = sender.Instigator; if (pawnSender == None) score = 0; else if (IsValidEnemy(pawnSender)) score = 0; return score; } // ---------------------------------------------------------------------- // DistressScore() // ---------------------------------------------------------------------- function float DistressScore(actor receiver, actor sender, float score) { local ScriptedPawn scriptedSender; local Pawn pawnSender; // Cull events received from enemies sender = InstigatorToPawn(sender); // hack pawnSender = Pawn(sender); scriptedSender = ScriptedPawn(sender); if (pawnSender == None) score = 0; else if ((GetPawnAllianceType(pawnSender) != ALLIANCE_Friendly) && !bFearDistress) score = 0; else if ((scriptedSender != None) && !scriptedSender.bDistressed) score = 0; return score; } // ---------------------------------------------------------------------- // UpdateReactionCallbacks() // ---------------------------------------------------------------------- function UpdateReactionCallbacks() { if (bReactFutz && bLookingForFutz) AISetEventCallback('Futz', 'HandleFutz', , true, true, false, true); else AIClearEventCallback('Futz'); if ((bHateHacking || bFearHacking) && bLookingForHacking) AISetEventCallback('MegaFutz', 'HandleHacking', , true, true, true, true); else AIClearEventCallback('MegaFutz'); if ((bHateWeapon || bFearWeapon) && bLookingForWeapon) AISetEventCallback('WeaponDrawn', 'HandleWeapon', 'WeaponDrawnScore', true, true, false, true); else AIClearEventCallback('WeaponDrawn'); if ((bReactShot || bFearShot || bHateShot) && bLookingForShot) AISetEventCallback('WeaponFire', 'HandleShot', , true, true, false, true); else AIClearEventCallback('WeaponFire'); /* if ((bHateCarcass || bReactCarcass || bFearCarcass) && bLookingForCarcass) AISetEventCallback('Carcass', 'HandleCarcass', 'CarcassScore', true, true, false, true); else AIClearEventCallback('Carcass'); */ if (bReactLoudNoise && bLookingForLoudNoise) { AISetEventCallback('LoudNoise', 'HandleLoudNoise', 'LoudNoiseScore'); //GMDX: more generic noise, like whats that a rock! AISetEventCallback('GenericNoise', 'HandleGenericNoise'); } else { AIClearEventCallback('LoudNoise'); AIClearEventCallback('GenericNoise'); } if ((bReactAlarm || bFearAlarm) && bLookingForAlarm) AISetEventCallback('Alarm', 'HandleAlarm'); else AIClearEventCallback('Alarm'); if ((bHateDistress || bReactDistress || bFearDistress) && bLookingForDistress) AISetEventCallback('Distress', 'HandleDistress', 'DistressScore', true, true, false, true); else AIClearEventCallback('Distress'); if ((bFearProjectiles || bReactProjectiles) && bLookingForProjectiles) AISetEventCallback('Projectile', 'HandleProjectiles', , false, true, false, true); else AIClearEventCallback('Projectile'); } // ---------------------------------------------------------------------- // SetReactions() // ---------------------------------------------------------------------- function SetReactions(bool bEnemy, bool bLoudNoise, bool bAlarm, bool bDistress, bool bProjectile, bool bFutz, bool bHacking, bool bShot, bool bWeapon, bool bCarcass, bool bInjury, bool bIndirectInjury) { bLookingForEnemy = bEnemy; bLookingForLoudNoise = bLoudNoise; bLookingForAlarm = bAlarm; bLookingForDistress = bDistress; bLookingForProjectiles = bProjectile; bLookingForFutz = bFutz; bLookingForHacking = bHacking; bLookingForShot = bShot; bLookingForWeapon = bWeapon; bLookingForCarcass = bCarcass; bLookingForInjury = bInjury; bLookingForIndirectInjury = bIndirectInjury; UpdateReactionCallbacks(); } // ---------------------------------------------------------------------- // BlockReactions() // ---------------------------------------------------------------------- function BlockReactions(optional bool bBlockInjury) { SetReactions(false, false, false, false, false, false, false, false, false, false, !bBlockInjury, !bBlockInjury); } // ---------------------------------------------------------------------- // ResetReactions() // ---------------------------------------------------------------------- function ResetReactions() { SetReactions(true, true, true, true, true, true, true, true, true, true, true, true); } // ---------------------------------------------------------------------- // HandleFutz() // ---------------------------------------------------------------------- function HandleFutz(Name event, EAIEventState state, XAIParams params) { local Pawn pawnActor; local DeusExPlayer dxPlayer; local AdaptiveArmor armor; if (bLookingForFutz && bReactFutz) { if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = GetPlayerPawn(); dxPlayer = DeusExPlayer(GetPlayerPawn()); if (pawnActor != None && dxPlayer != none && dxPlayer.AugmentationSystem.GetAugLevelValue(class'AugCloak') <= -1.0) { foreach AllActors(class'AdaptiveArmor', armor) //CyberP: thermoptic camo hides us from detection if ((armor.Owner == dxPlayer) && armor.bActive) return; ReactToFutz(); // only players can futz if (dxplayer.bHardcoreAI3 && FRand() < 0.4) { if (!IsA('ThugMale') && !IsA('NicoletteDuClare') && (IsA('HumanThug') || IsA('HumanMilitary'))) { IncreaseAgitation(pawnActor, 1.0); if (SetEnemy(pawnActor)) { SetDistressTimer(); HandleEnemy(); } } } } } } } // ---------------------------------------------------------------------- // HandleHacking() // ---------------------------------------------------------------------- function HandleHacking(Name event, EAIEventState state, XAIParams params) { // Fear, Hate local Pawn pawnActor; local DeusExPlayer dxPlayer; local AdaptiveArmor armor; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = GetPlayerPawn(); dxPlayer = DeusExPlayer(GetPlayerPawn()); if (pawnActor != None && dxPlayer != none && dxPlayer.AugmentationSystem.GetAugLevelValue(class'AugCloak') <= -1.0) { foreach AllActors(class'AdaptiveArmor', armor) //CyberP: thermoptic camo hides us if ((armor.Owner == dxPlayer) && armor.bActive) return; if (bHateHacking) IncreaseAgitation(pawnActor, 1.0); if (bFearHacking) IncreaseFear(pawnActor, 0.51); if (SetEnemy(pawnActor)) { SetDistressTimer(); HandleEnemy(); } else if (bFearHacking && IsFearful()) { SetDistressTimer(); SetEnemy(pawnActor, , true); GotoState('Fleeing'); } else // only players can hack ReactToFutz(); } } } // ---------------------------------------------------------------------- // HandleWeapon() // ---------------------------------------------------------------------- function HandleWeapon(Name event, EAIEventState state, XAIParams params) { // Fear, Hate local Pawn pawnActor; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = InstigatorToPawn(params.bestActor); if (pawnActor != None) { if (bHateWeapon) IncreaseAgitation(pawnActor); if (bFearWeapon) IncreaseFear(pawnActor, 1.0); // Let presence checking handle enemy sighting if (!IsValidEnemy(pawnActor)) { if (bFearWeapon && IsFearful()) { SetDistressTimer(); SetEnemy(pawnActor, , true); GotoState('Fleeing'); } else if (pawnActor.bIsPlayer) ReactToFutz(); } } } } // ---------------------------------------------------------------------- // HandleShot() // ---------------------------------------------------------------------- function HandleShot(Name event, EAIEventState state, XAIParams params) { // React, Fear, Hate local Pawn pawnActor; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = InstigatorToPawn(params.bestActor); if (pawnActor != None) { if (bHateShot) IncreaseAgitation(pawnActor); if (bFearShot) IncreaseFear(pawnActor, 1.0); if (SetEnemy(pawnActor)) { SetDistressTimer(); HandleEnemy(); } else if (bFearShot && IsFearful()) { SetDistressTimer(); SetEnemy(pawnActor, , true); GotoState('Fleeing'); } else if (pawnActor.bIsPlayer) ReactToFutz(); } } } // ---------------------------------------------------------------------- // HandleLoudNoise() // ---------------------------------------------------------------------- function HandleLoudNoise(Name event, EAIEventState state, XAIParams params) { // React local Actor bestActor; local Pawn instigator; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if (bestActor != None) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { if (IsValidEnemy(instigator)) { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); HandleEnemy(); } } } } } // ---------------------------------------------------------------------- // GMDX: HandleGenericNoise() // ---------------------------------------------------------------------- function HandleGenericNoise(Name event, EAIEventState state, XAIParams params) { // React local Actor bestActor; local Pawn instigator; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if (bestActor != None) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { // if (IsValidEnemy(instigator)) // { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); if (Enemy == None) GotoState('Seeking','GoToLocation'); else if (RaiseAlarm == RAISEALARM_BeforeAttacking) GotoState('Alerting'); // else // GotoState('Attacking'); // } } } } } // ---------------------------------------------------------------------- // HandleProjectiles() // ---------------------------------------------------------------------- function HandleProjectiles(Name event, EAIEventState state, XAIParams params) { // React, Fear local DeusExProjectile dxProjectile; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) if (params.bestActor != None) ReactToProjectiles(params.bestActor); } // ---------------------------------------------------------------------- // HandleAlarm() // ---------------------------------------------------------------------- function HandleAlarm(Name event, EAIEventState state, XAIParams params) { // React, Fear local AlarmUnit alarm; local LaserTrigger laser; local SecurityCamera camera; local Computers computer; local Pawn alarmInstigator; local vector alarmLocation; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { alarmInstigator = None; alarm = AlarmUnit(params.bestActor); laser = LaserTrigger(params.bestActor); camera = SecurityCamera(params.bestActor); computer = Computers(params.bestActor); if (alarm != None) { alarmInstigator = alarm.alarmInstigator; alarmLocation = alarm.alarmLocation; } else if (laser != None) { alarmInstigator = Pawn(laser.triggerActor); if (alarmInstigator == None) alarmInstigator = laser.triggerActor.Instigator; alarmLocation = laser.actorLocation; } else if (camera != None) { alarmInstigator = GetPlayerPawn(); // player is implicit for cameras alarmLocation = camera.playerLocation; } else if (computer != None) { alarmInstigator = GetPlayerPawn(); // player is implicit for computers alarmLocation = computer.Location; } if (bFearAlarm) { IncreaseFear(alarmInstigator, 2.0); if (IsFearful()) { SetDistressTimer(); SetEnemy(alarmInstigator, , true); GotoState('Fleeing'); } } if (alarmInstigator != None) { if (alarmInstigator.Health > 0) { if (IsValidEnemy(alarmInstigator)) { AlarmTimer = 120; SetDistressTimer(); if (IsA('HumanMilitary') && !bAlarmStatIncrease) //CyberP: AI get better stats if alarm is triggered. { bAlarmStatIncrease=True; if (SurprisePeriod > 0.5) SurprisePeriod -= 0.100000; if (VisibilityThreshold > 0.002000) VisibilityThreshold -= 0.000500; if (BaseAccuracy > 0.010000) BaseAccuracy -= 0.010000; //CyberP: So, the result is improved accuracy, reload speed, cooldown time, } //vision & reaction time. if (FRand() < 0.5) SetSeekLocation(alarmInstigator, alarmLocation, SEEKTYPE_Guess, true); //CyberP: different reaction else SetSeekLocation(alarmInstigator, alarmLocation, SEEKTYPE_Sound); HandleEnemy(); } } } } } // ---------------------------------------------------------------------- // HandleDistress() // ---------------------------------------------------------------------- function HandleDistress(Name event, EAIEventState state, XAIParams params) { // React, Fear, Hate local float seeTime; local Pawn distressee; local DeusExPlayer distresseePlayer; local ScriptedPawn distresseePawn; local Pawn distressor; local DeusExPlayer distressorPlayer; local ScriptedPawn distressorPawn; local bool bDistresseeValid; local bool bDistressorValid; local float distressVal; local name stateName; local bool bAttacking; local bool bFleeing; bAttacking = false; seeTime = 0; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { distressee = InstigatorToPawn(params.bestActor); if (distressee != None) { if (bFearDistress) IncreaseFear(distressee.Enemy, 1.0); bDistresseeValid = false; bDistressorValid = false; distresseePlayer = DeusExPlayer(distressee); distresseePawn = ScriptedPawn(distressee); if (GetPawnAllianceType(distressee) == ALLIANCE_Friendly) { if (distresseePawn != None) { if (distresseePawn.bDistressed && (distresseePawn.EnemyLastSeen <= EnemyTimeout)) { bDistresseeValid = true; seeTime = distresseePawn.EnemyLastSeen; } } else if (distresseePlayer != None) bDistresseeValid = true; } if (bDistresseeValid) { distressor = distressee.Enemy; distressorPlayer = DeusExPlayer(distressor); distressorPawn = ScriptedPawn(distressor); if (distressorPawn != None) { if (bHateDistress || (distressorPawn.GetPawnAllianceType(distressee) == ALLIANCE_Hostile)) bDistressorValid = true; } else if (distresseePawn != None) { if (bHateDistress || (distresseePawn.GetPawnAllianceType(distressor) == ALLIANCE_Hostile)) bDistressorValid = true; } // Finally, react if (bDistressorValid) { if (bHateDistress) IncreaseAgitation(distressor, 1.0); if (SetEnemy(distressor, seeTime)) { SetDistressTimer(); HandleEnemy(); bAttacking = true; } } // BOOGER! Make NPCs react by seeking if distressor isn't an enemy? } if (!bAttacking && bFearDistress) { distressVal = 0; bFleeing = false; if (distresseePawn != None) { stateName = distresseePawn.GetStateName(); if (stateName == 'Fleeing') // hack -- to prevent infinite fleeing { if (distresseePawn.DistressTimer >= 0) { if (FearSustainTime - distresseePawn.DistressTimer >= 1) { IncreaseFear(distressee.Enemy, 1.0, distresseePawn.DistressTimer); distressVal = distresseePawn.DistressTimer; bFleeing = true; } } } else { IncreaseFear(distressee.Enemy, 1.0); bFleeing = true; } } else { IncreaseFear(distressee.Enemy, 1.0); bFleeing = true; } if (bFleeing && IsFearful()) { if ((DistressTimer > distressVal) || (DistressTimer < 0)) DistressTimer = distressVal; SetEnemy(distressee.Enemy, , true); GotoState('Fleeing'); } } } } } // ---------------------------------------------------------------------- // IncreaseFear() // ---------------------------------------------------------------------- function IncreaseFear(Actor actorInstigator, float addedFearLevel, optional float newFearTimer) { local DeusExPlayer player; local Pawn instigator; instigator = InstigatorToPawn(actorInstigator); if (instigator != None) { if (FearTimer < (FearSustainTime-newFearTimer)) FearTimer = FearSustainTime-newFearTimer; if (FearTimer > 0) { if (addedFearLevel > 0) { FearLevel += addedFearLevel; if (FearLevel > 1.0) FearLevel = 1.0; } } } } // ---------------------------------------------------------------------- // IncreaseAgitation() // ---------------------------------------------------------------------- function IncreaseAgitation(Actor actorInstigator, optional float AgitationLevel) { local Pawn instigator; local float minLevel; instigator = InstigatorToPawn(actorInstigator); if (instigator != None) { AgitationTimer = AgitationSustainTime; if (AgitationCheckTimer <= 0) { AgitationCheckTimer = 1.5; // hardcoded for now if (AgitationLevel == 0) { if (MaxProvocations < 0) MaxProvocations = 0; AgitationLevel = 1.0/(MaxProvocations+1); } if (AgitationLevel > 0) { bAlliancesChanged = True; bNoNegativeAlliances = False; AgitateAlliance(instigator.Alliance, AgitationLevel); } } } } // ---------------------------------------------------------------------- // DecreaseAgitation() // ---------------------------------------------------------------------- function DecreaseAgitation(Actor actorInstigator, float AgitationLevel) { local float newLevel; local DeusExPlayer player; local Pawn instigator; player = DeusExPlayer(GetPlayerPawn()); if (Inventory(actorInstigator) != None) { if (Inventory(actorInstigator).Owner != None) actorInstigator = Inventory(actorInstigator).Owner; } else if (DeusExDecoration(actorInstigator) != None) actorInstigator = player; instigator = Pawn(actorInstigator); if ((instigator == None) || (instigator == self)) return; AgitationTimer = AgitationSustainTime; if (AgitationLevel > 0) { bAlliancesChanged = True; bNoNegativeAlliances = False; AgitateAlliance(instigator.Alliance, -AgitationLevel); } } // ---------------------------------------------------------------------- // UpdateAgitation() // ---------------------------------------------------------------------- function UpdateAgitation(float deltaSeconds) { local float mult; local float decrement; local int i; if (AgitationCheckTimer > 0) { AgitationCheckTimer -= deltaSeconds; if (AgitationCheckTimer < 0) AgitationCheckTimer = 0; } decrement = 0; if (AgitationTimer > 0) { if (AgitationTimer < deltaSeconds) { mult = 1.0 - (AgitationTimer/deltaSeconds); AgitationTimer = 0; decrement = mult * (AgitationDecayRate*deltaSeconds); } else AgitationTimer -= deltaSeconds; } else decrement = AgitationDecayRate*deltaSeconds; if (bAlliancesChanged && (decrement > 0)) { bAlliancesChanged = False; for (i=15; i>=0; i--) { if ((AlliancesEx[i].AllianceName != '') && (!AlliancesEx[i].bPermanent)) { if (AlliancesEx[i].AgitationLevel > 0) { bAlliancesChanged = true; AlliancesEx[i].AgitationLevel -= decrement; if (AlliancesEx[i].AgitationLevel < 0) AlliancesEx[i].AgitationLevel = 0; } } } } } // ---------------------------------------------------------------------- // UpdateFear() // ---------------------------------------------------------------------- function UpdateFear(float deltaSeconds) { local float mult; local float decrement; local int i; decrement = 0; if (FearTimer > 0) { if (FearTimer < deltaSeconds) { mult = 1.0 - (FearTimer/deltaSeconds); FearTimer = 0; decrement = mult * (FearDecayRate*deltaSeconds); } else FearTimer -= deltaSeconds; } else decrement = FearDecayRate*deltaSeconds; if ((decrement > 0) && (FearLevel > 0)) { FearLevel -= decrement; if (FearLevel < 0) FearLevel = 0; } } // ---------------------------------------------------------------------- // IsFearful() // ---------------------------------------------------------------------- function bool IsFearful() { if (FearLevel >= 1.0) return true; else return false; } // ---------------------------------------------------------------------- // ShouldBeStartled() [stub function, overridden by subclasses] // ---------------------------------------------------------------------- function bool ShouldBeStartled(Pawn startler) { return false; } // ---------------------------------------------------------------------- // ShouldPlayTurn() // ---------------------------------------------------------------------- function bool ShouldPlayTurn(vector lookdir) { local Rotator rot; rot = Rotator(lookdir); rot.Yaw = (rot.Yaw - Rotation.Yaw) & 65535; if (rot.Yaw > 32767) rot.Yaw = 65536 - rot.Yaw; // negate if (rot.Yaw > 4096) return true; else return false; } // ---------------------------------------------------------------------- // ShouldPlayWalk() // ---------------------------------------------------------------------- function bool ShouldPlayWalk(vector movedir) { local vector diff; if (Physics == PHYS_Falling) return true; else if (Physics == PHYS_Walking) { diff = (movedir - Location) * vect(1,1,0); if (VSize(diff) < 16) return false; else return true; } else if (VSize(movedir-Location) < 16) return false; else return true; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // ALLIANCE ROUTINES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // SetAlliance() // ---------------------------------------------------------------------- function SetAlliance(Name newAlliance) { Alliance = newAlliance; } // ---------------------------------------------------------------------- // ChangeAlly() // ---------------------------------------------------------------------- function ChangeAlly(Name newAlly, optional float allyLevel, optional bool bPermanent, optional bool bHonorPermanence) { local int i; // Members of the same alliance will ALWAYS be friendly to each other if (newAlly == Alliance) { allyLevel = 1; bPermanent = true; } if (bHonorPermanence) { for (i=0; i<16; i++) if (AlliancesEx[i].AllianceName == newAlly) if (AlliancesEx[i].bPermanent) break; if (i < 16) return; } if (allyLevel < -1) allyLevel = -1; if (allyLevel > 1) allyLevel = 1; for (i=0; i<16; i++) if ((AlliancesEx[i].AllianceName == newAlly) || (AlliancesEx[i].AllianceName == '')) break; if (i >= 16) for (i=15; i>0; i--) AlliancesEx[i] = AlliancesEx[i-1]; AlliancesEx[i].AllianceName = newAlly; AlliancesEx[i].AllianceLevel = allyLevel; AlliancesEx[i].AgitationLevel = 0; AlliancesEx[i].bPermanent = bPermanent; bAlliancesChanged = True; bNoNegativeAlliances = False; } // ---------------------------------------------------------------------- // AgitateAlliance() // ---------------------------------------------------------------------- function AgitateAlliance(Name newEnemy, float agitation) { local int i; local float oldLevel; local float newLevel; if (newEnemy != '') { for (i=0; i<16; i++) if ((AlliancesEx[i].AllianceName == newEnemy) || (AlliancesEx[i].AllianceName == '')) break; if (i < 16) { if ((AlliancesEx[i].AllianceName == '') || !(AlliancesEx[i].bPermanent)) { if (AlliancesEx[i].AllianceName == '') AlliancesEx[i].AllianceLevel = 0; oldLevel = AlliancesEx[i].AgitationLevel; newLevel = oldLevel + agitation; if (newLevel > 1.0) newLevel = 1.0; AlliancesEx[i].AllianceName = newEnemy; AlliancesEx[i].AgitationLevel = newLevel; if ((newEnemy == 'Player') && (oldLevel < 1.0) && (newLevel >= 1.0)) // hack PlayerAgitationTimer = 2.0; bAlliancesChanged = True; } } } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // ATTACKING FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // AISafeToShoot() // ---------------------------------------------------------------------- function bool AISafeToShoot(out Actor hitActor, vector traceEnd, vector traceStart, optional vector extent, optional bool bIgnoreLevel) { local Actor traceActor; local Vector hitLocation; local Vector hitNormal; local Pawn tracePawn; local DeusExDecoration traceDecoration; local DeusExMover traceMover; local bool bSafe; //local float dista; // Future improvement: // Ideally, this should use the ammo type to determine how many shots // it will take to destroy an obstruction, and call it unsafe if it takes // more than x shots. Also, if the ammo is explosive, and the // obstruction is too close, it should never be safe... bSafe = true; hitActor = None; foreach TraceActors(Class'Actor', traceActor, hitLocation, hitNormal, traceEnd, traceStart, extent) { if (hitActor == None) hitActor = traceActor; if (traceActor == Level) { //dista = Abs(VSize(Enemy.Location - traceActor.Location)); //CyberP: supressive fire //if (dista < 512) // break; if (!bIgnoreLevel) bSafe = false; break; } tracePawn = Pawn(traceActor); if (tracePawn != None) { if (tracePawn != self) { if (GetPawnAllianceType(tracePawn) == ALLIANCE_Friendly && !tracePawn.IsInState('Dying')) //CyberP: dying pawns are ignored. bSafe = false; break; } } traceDecoration = DeusExDecoration(traceActor); if (traceDecoration != None) { if (traceDecoration.bExplosive || traceDecoration.bInvincible) { bSafe = false; break; } //if ((traceDecoration.HitPoints > 100) || (traceDecoration.minDamageThreshold > 20)) // hack //CyberP: hp was 20, dt was 4 //CyberP: actually, comment out //{ // bSafe = false; // break; //} } traceMover = DeusExMover(traceActor); if (traceMover != None) { if (!traceMover.bBreakable) { bSafe = false; break; } else if ((traceMover.minDamageThreshold > 3)) // hack //CyberP: get them to shoot through breakable glass { bSafe = false; break; } else // hack break; } //if (Inventory(traceActor) != None) //CyberP: cut this out since inventory is breakable or moves when shot //{ // bSafe = false; // break; //} } return (bSafe); } // ---------------------------------------------------------------------- // ComputeThrowAngles() // ---------------------------------------------------------------------- function bool ComputeThrowAngles(vector traceEnd, vector traceStart, float speed, out Rotator angle1, out Rotator angle2) { local float deltaX, deltaY; local float x, y; local float tanAngle1, tanAngle2; local float A, B, C; local float m, n; local float sqrtTerm; local float gravity; local float traceYaw; local bool bValid; bValid = false; // Reduce our problem to two dimensions deltaX = traceEnd.X - traceStart.X; deltaY = traceEnd.Y - traceStart.Y; x = sqrt(deltaX*deltaX + deltaY*deltaY); y = traceEnd.Z - traceStart.Z; gravity = -Region.Zone.ZoneGravity.Z; if ((x > 0) && (gravity > 0)) { A = -gravity*x*x; B = 2*speed*speed*x; C = -gravity*x*x - 2*y*speed*speed; sqrtTerm = B*B - 4*A*C; if (sqrtTerm >= 0) { m = -B/(2*A); n = sqrt(sqrtTerm)/(2*A); tanAngle1 = atan(m+n); tanAngle2 = atan(m-n); angle1 = Rotator(traceEnd - traceStart); angle2 = angle1; angle1.Pitch = tanAngle1*32768/Pi; angle2.Pitch = tanAngle2*32768/Pi; bValid = true; } } return bValid; } // ---------------------------------------------------------------------- // AISafeToThrow() // ---------------------------------------------------------------------- function bool AISafeToThrow(vector traceEnd, vector traceStart, float throwAccuracy, optional vector extent) { local float time1, time2, tempTime; local vector pos1, pos2, tempPos; local rotator rot1, rot2, tempRot; local rotator bestAngle; local bool bSafe; local DeusExWeapon dxWeapon; local Class<ThrownProjectile> throwClass; // Someday, we should check for nearby friendlies within the blast radius // before throwing... // Sanity checks throwClass = None; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) throwClass = Class<ThrownProjectile>(dxWeapon.ProjectileClass); if (throwClass == None) return false; if (extent == vect(0,0,0)) { extent = vect(1,1,0) * throwClass.Default.CollisionRadius; extent.Z = throwClass.Default.CollisionHeight; } if (throwAccuracy < 0.01) throwAccuracy = 0.01; bSafe = false; if (ComputeThrowAngles(traceEnd, traceStart, dxWeapon.ProjectileSpeed, rot1, rot2)) { time1 = ParabolicTrace(pos1, Vector(rot1)*dxWeapon.ProjectileSpeed, traceStart, true, extent, 5.0, throwClass.Default.Elasticity, throwClass.Default.bBounce, 60, throwAccuracy); time2 = ParabolicTrace(pos2, Vector(rot2)*dxWeapon.ProjectileSpeed, traceStart, true, extent, 5.0, throwClass.Default.Elasticity, throwClass.Default.bBounce, 60, throwAccuracy); if ((time1 > 0) || (time2 > 0)) { if ((time1 > time2) && (time2 > 0)) { tempTime = time1; time1 = time2; time2 = tempTime; tempPos = pos1; pos1 = pos2; pos2 = tempPos; tempRot = rot1; rot1 = rot2; rot2 = tempRot; } if (VSize(pos1-traceEnd) <= throwClass.Default.blastRadius) { if (FastTrace(traceEnd, pos1)) { if ((VSize(pos1-Location) > throwClass.Default.blastRadius*0.5) || !FastTrace(Location, pos1)) { bestAngle = rot1; bSafe = true; } } } } if (!bSafe && (time2 > 0)) { if (VSize(pos2-traceEnd) <= throwClass.Default.blastRadius) { if (FastTrace(traceEnd, pos2)) { if ((VSize(pos2-Location) > throwClass.Default.blastRadius*0.5) || !FastTrace(Location, pos2)) { bestAngle = rot2; bSafe = true; } } } } } if (bSafe) ViewRotation = bestAngle; return (bSafe); } // ---------------------------------------------------------------------- // AICanShoot() // ---------------------------------------------------------------------- function bool AICanShoot(pawn target, bool bLeadTarget, bool bCheckReadiness, optional float throwAccuracy, optional bool bDiscountMinRange) { local DeusExWeapon dxWeapon; local Vector X, Y, Z; local Vector projStart, projEnd; local float tempMinRange, tempMaxRange; local float temp; local float dist; local float extraDist; local actor hitActor; local Vector hitLocation, hitNormal; local Vector extent; local bool bIsThrown; local float elevation; local bool bSafe; if (target == None) return false; if (target.bIgnore) return false; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon == None) return false; if (bCheckReadiness && !dxWeapon.bReadyToFire) return false; if (dxWeapon.ReloadCount > 0) { if (dxWeapon.AmmoType == None) return false; if (dxWeapon.AmmoType.AmmoAmount <= 0) return false; } if (FireElevation > 0) { elevation = FireElevation + (CollisionHeight+target.CollisionHeight); if (elevation < 10) elevation = 10; if (Abs(Location.Z-target.Location.Z) > elevation) return false; } bIsThrown = IsThrownWeapon(dxWeapon); extraDist = target.CollisionRadius; //extraDist = 0; GetPawnWeaponRanges(self, tempMinRange, tempMaxRange, temp); if (bDiscountMinRange) tempMinRange = 0; if (tempMinRange >= tempMaxRange) return false; ViewRotation = Rotation; GetAxes(ViewRotation, X, Y, Z); projStart = dxWeapon.ComputeProjectileStart(X, Y, Z); if (bLeadTarget && !dxWeapon.bInstantHit && (dxWeapon.ProjectileSpeed > 0)) { if (bIsThrown) { // compute target's position 1.5 seconds in the future projEnd = target.Location + (target.Velocity*1.5); } else { // projEnd = target.Location + (target.Velocity*dist/dxWeapon.ProjectileSpeed); if (!ComputeTargetLead(target, projStart, dxWeapon.ProjectileSpeed, 5.0, projEnd)) return false; } } else projEnd = target.Location; if (bIsThrown) projEnd += vect(0,0,-1)*(target.CollisionHeight-5); dist = VSize(projEnd - Location); if (dist < 0) dist = 0; if ((dist < tempMinRange) || (dist-extraDist > tempMaxRange)) return false; if (!bIsThrown) { bSafe = FastTrace(target.Location, projStart); if (!bSafe && target.bIsPlayer) // players only... hack { projEnd += vect(0,0,1)*target.BaseEyeHeight; bSafe = FastTrace(target.Location + vect(0,0,1)*target.BaseEyeHeight, projStart); } if (!bSafe) return false; } if (dxWeapon.bInstantHit) return (AISafeToShoot(hitActor, projEnd, projStart, , true)); else { extent.X = dxWeapon.ProjectileClass.default.CollisionRadius; extent.Y = dxWeapon.ProjectileClass.default.CollisionRadius; extent.Z = dxWeapon.ProjectileClass.default.CollisionHeight; if (bIsThrown && (throwAccuracy > 0)) return (AISafeToThrow(projEnd, projStart, throwAccuracy, extent)); else if (dxWeapon.IsA('WeaponPlasmaRifle')) //CyberP: so plasma rifle users can attack close range return (AISafeToShoot(hitActor, projEnd, projStart, extent*1.1)); else return (AISafeToShoot(hitActor, projEnd, projStart, extent*3)); } } // ---------------------------------------------------------------------- // ComputeTargetLead() // ---------------------------------------------------------------------- function bool ComputeTargetLead(pawn target, vector projectileStart, float projectileSpeed, float maxTime, out Vector hitPos) { local vector targetLoc; local vector targetVel; local float termA, termB, termC; local float temp; local float base, range; local float time1, time2; local bool bSuccess; bSuccess = true; targetLoc = target.Location - projectileStart; targetVel = target.Velocity; if (target.Physics == PHYS_Falling) targetVel.Z = 0; // Given a target position and velocity, and a projectile speed, // compute the position at which a projectile will hit the // target if the target continues at its current velocity // (Warning: messy computations follow. I can't believe I remembered // enough algebra to figure this out on my own... :) termA = targetVel.X*targetVel.X + targetVel.Y*targetVel.Y + targetVel.Z*targetVel.Z - projectileSpeed*projectileSpeed; termB = 2*targetLoc.X*targetVel.X + 2*targetLoc.Y*targetVel.Y + 2*targetLoc.Z*targetVel.Z; termC = targetLoc.X*targetLoc.X + targetLoc.Y*targetLoc.Y + targetLoc.Z*targetLoc.Z; if ((termA < 0.000001) && (termA > -0.000001)) // avoid divide-by-zero errors... termA = 0.000001; // fudge a little when velocities are equal temp = termB*termB - 4*termA*termC; if (temp < 0) bSuccess = false; if (bSuccess) { base = -termB/(2*termA); range = sqrt(temp)/(2*termA); time1 = base+range; time2 = base-range; if ((time1 > time2) || (time1 < 0)) // best time first time1 = time2; if ((time1 < 0) || (time1 >= maxTime)) bSuccess = false; } if (bSuccess) hitPos = target.Location + target.Velocity*time1; return (bSuccess); } // ---------------------------------------------------------------------- // GetPawnWeaponRanges() // ---------------------------------------------------------------------- function GetPawnWeaponRanges(Pawn other, out float minRange, out float maxAccurateRange, out float maxRange) { local DeusExWeapon pawnWeapon; local Class<DeusExProjectile> projectileClass; pawnWeapon = DeusExWeapon(other.Weapon); if (pawnWeapon != None) { pawnWeapon.GetWeaponRanges(minRange, maxAccurateRange, maxRange); if (IsThrownWeapon(pawnWeapon)) // hack minRange = 0; } else { minRange = 0; maxAccurateRange = other.CollisionRadius; maxRange = maxAccurateRange; } if (maxAccurateRange > maxRange) maxAccurateRange = maxRange; if (minRange > maxRange) minRange = maxRange; } // ---------------------------------------------------------------------- // GetWeaponBestRange() // ---------------------------------------------------------------------- function GetWeaponBestRange(DeusExWeapon dxWeapon, out float bestRangeMin, out float bestRangeMax) { local float temp; local float minRange, maxRange; local float AIMinRange, AIMaxRange; if (dxWeapon != None) { dxWeapon.GetWeaponRanges(minRange, maxRange, temp); if (IsThrownWeapon(dxWeapon)) // hack minRange = 0; AIMinRange = dxWeapon.AIMinRange; AIMaxRange = dxWeapon.AIMaxRange; if ((AIMinRange > 0) && (AIMinRange >= minRange) && (AIMinRange <= maxRange)) bestRangeMin = AIMinRange; else bestRangeMin = minRange; if ((AIMaxRange > 0) && (AIMaxRange >= minRange) && (AIMaxRange <= maxRange)) bestRangeMax = AIMaxRange; else bestRangeMax = maxRange; if (bestRangeMin > bestRangeMax) bestRangeMin = bestRangeMax; } else { bestRangeMin = 0; bestRangeMax = 0; } } // ---------------------------------------------------------------------- // ReadyForNewEnemy() // ---------------------------------------------------------------------- function bool ReadyForNewEnemy() { if ((Enemy == None) || (EnemyTimer > 5.0)) return True; else return False; } // ---------------------------------------------------------------------- // CheckEnemyParams() [internal use only] // ---------------------------------------------------------------------- function CheckEnemyParams(Pawn checkPawn, out Pawn bestPawn, out int bestThreatLevel, out float bestDist) { local ScriptedPawn sPawn; local bool bReplace; local float dist; local int threatLevel; local bool bValid; bValid = IsValidEnemy(checkPawn); if (bValid && (Enemy != checkPawn)) { // Honor cloaking, radar transparency, and other augs if this guy isn't our current enemy if (ComputeActorVisibility(checkPawn) < 0.1) bValid = false; } if (bValid) { sPawn = ScriptedPawn(checkPawn); dist = VSize(checkPawn.Location - Location); if (checkPawn.IsA('Robot')) dist *= 0.5; // arbitrary if (Enemy == checkPawn) dist *= 0.75; // arbitrary if (sPawn != None) { if (sPawn.bAttacking) { if (sPawn.Enemy == self) threatLevel = 2; else threatLevel = 1; } else if (sPawn.GetStateName() == 'Alerting') threatLevel = 3; else if ((sPawn.GetStateName() == 'Fleeing') || (sPawn.GetStateName() == 'Burning')) threatLevel = 0; else if (sPawn.Weapon != None) threatLevel = 1; else threatLevel = 0; } else // player { if (checkPawn.Weapon != None) threatLevel = 2; else threatLevel = 1; } bReplace = false; if (bestPawn == None) bReplace = true; else if (bestThreatLevel < threatLevel) bReplace = true; else if (bestDist > dist) bReplace = true; if (bReplace) { if ((Enemy == checkPawn) || (AICanSee(checkPawn, , false, false, true, true) > 0)) { bestPawn = checkPawn; bestThreatLevel = threatLevel; bestDist = dist; } } } } // ---------------------------------------------------------------------- // FindBestEnemy() // ---------------------------------------------------------------------- function FindBestEnemy(bool bIgnoreCurrentEnemy) { local Pawn nextPawn; local Pawn bestPawn; local float bestDist; local int bestThreatLevel; local float newSeenTime; bestPawn = None; bestDist = 0; bestThreatLevel = 0; if (!bIgnoreCurrentEnemy && (Enemy != None)) CheckEnemyParams(Enemy, bestPawn, bestThreatLevel, bestDist); foreach RadiusActors(Class'Pawn', nextPawn, 2000) // arbitrary if (enemy != nextPawn) CheckEnemyParams(nextPawn, bestPawn, bestThreatLevel, bestDist); if (bestPawn != Enemy) newSeenTime = 0; else newSeenTime = EnemyLastSeen; SetEnemy(bestPawn, newSeenTime, true); EnemyTimer = 0; } // ---------------------------------------------------------------------- // ShouldStrafe() // ---------------------------------------------------------------------- function bool ShouldStrafe() { // This may be overridden from subclasses //return (AICanSee(enemy, 1.0, false, true, true, true) > 0); return (AICanShoot(enemy, false, false, 0.025, true)); } // ---------------------------------------------------------------------- // ShouldFlee() // ---------------------------------------------------------------------- function bool ShouldFlee() { // This may be overridden from subclasses if (MinHealth > 0) { if (Health <= MinHealth) return true; else if (HealthArmLeft <= 0) return true; else if (HealthArmRight <= 0) return true; else if (HealthLegLeft <= 0) return true; else if (HealthLegRight <= 0) return true; else return false; } else return false; } // ---------------------------------------------------------------------- // ShouldDropWeapon() // ---------------------------------------------------------------------- function bool ShouldDropWeapon() { if (isA('MIB')||isA('WIB')) return false; if (((HealthArmLeft <= 0) || (HealthArmRight <= 0)) && (Health > 0)) return true; else return false; } // ---------------------------------------------------------------------- // TryLocation() // ---------------------------------------------------------------------- function bool TryLocation(out vector position, optional float minDist, optional bool bTraceActors, optional NearbyProjectileList projList) { local float magnitude; local vector normalPos; local Rotator rot; local float dist; local bool bSuccess; normalPos = position-Location; magnitude = VSize(normalPos); if (minDist > magnitude) minDist = magnitude; rot = Rotator(position-Location); bSuccess = AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, position); if (bSuccess) { if (bDefendHome && !IsNearHome(position)) bSuccess = false; else if (bAvoidHarm && IsLocationDangerous(projList, position)) bSuccess = false; } return (bSuccess); } // ---------------------------------------------------------------------- // ComputeBestFiringPosition() // ---------------------------------------------------------------------- function EDestinationType ComputeBestFiringPosition(out vector newPosition) { local float selfMinRange, selfMaxRange; local float enemyMinRange, enemyMaxRange; local float temp; local float dist; local float innerRange[2], outerRange[2]; local Rotator relativeRotation; local float hAngle, vAngle; local int acrossDist; local float awayDist; local float extraDist; local float fudgeMargin; local int angle; local float maxDist; local float distDelta; local bool bInnerValid, bOuterValid; local vector tryVector; local EDestinationType destType; local float moveMult; local float reloadMult; local float minArea; local float minDist; local float range; local float margin; local NearbyProjectileList projList; local vector projVector; local bool bUseProjVector; local rotator sprintRot; local vector sprintVect; local bool bUseSprint; destType = DEST_Failure; extraDist = enemy.CollisionRadius*0.5; fudgeMargin = 100; minArea = 35; GetPawnWeaponRanges(self, selfMinRange, selfMaxRange, temp); GetPawnWeaponRanges(enemy, enemyMinRange, temp, enemyMaxRange); if (selfMaxRange > 1200) selfMaxRange = 1200; if (enemyMaxRange > 1200) enemyMaxRange = 1200; // hack, to prevent non-strafing NPCs from trying to back up if (!bCanStrafe) selfMinRange = 0; minDist = enemy.CollisionRadius + CollisionRadius - (extraDist+1); if (selfMinRange < minDist) selfMinRange = minDist; if (selfMinRange < MinRange) selfMinRange = MinRange; if (selfMaxRange > MaxRange) selfMaxRange = MaxRange; dist = VSize(enemy.Location-Location); innerRange[0] = selfMinRange; innerRange[1] = selfMaxRange; outerRange[0] = selfMinRange; outerRange[1] = selfMaxRange; // hack, to prevent non-strafing NPCs from trying to back up if (selfMaxRange > enemyMinRange) innerRange[1] = enemyMinRange; if ((selfMinRange < enemyMaxRange) && bCanStrafe) // hack, to prevent non-strafing NPCs from trying to back up outerRange[0] = enemyMaxRange; range = outerRange[1]-outerRange[0]; if (range < minArea) { outerRange[0] = 0; outerRange[1] = 0; } range = innerRange[1]-innerRange[0]; if (range < minArea) { innerRange[0] = outerRange[0]; innerRange[1] = outerRange[1]; outerRange[0] = 0; outerRange[1] = 0; } // If the enemy can reach us through our entire weapon range, just use the range if ((innerRange[0] >= innerRange[1]) && (outerRange[0] >= outerRange[1])) { innerRange[0] = selfMinRange; innerRange[1] = selfMaxRange; } innerRange[0] += extraDist; innerRange[1] += extraDist; outerRange[0] += extraDist; outerRange[1] += extraDist; if (innerRange[0] >= innerRange[1]) bInnerValid = false; else bInnerValid = true; if (outerRange[0] >= outerRange[1]) bOuterValid = false; else bOuterValid = true; if (!bInnerValid) { // ugly newPosition = Location; // return DEST_SameLocation; return destType; } relativeRotation = Rotator(Location - enemy.Location); hAngle = (relativeRotation.Yaw - enemy.Rotation.Yaw) & 65535; if (hAngle > 32767) hAngle -= 65536; // ignore vertical angle for now awayDist = dist; acrossDist = 0; maxDist = GroundSpeed*0.6; // distance covered in 6/10 second if (bInnerValid) { margin = (innerRange[1]-innerRange[0]) * 0.5; if (margin > fudgeMargin) margin = fudgeMargin; if (awayDist < innerRange[0]) awayDist = innerRange[0]+margin; else if (awayDist > innerRange[1]) awayDist = innerRange[1]-margin; } if (bOuterValid) { margin = (outerRange[1]-outerRange[0]) * 0.5; if (margin > fudgeMargin) margin = fudgeMargin; if (awayDist > outerRange[1]) awayDist = outerRange[1]-margin; } if (awayDist > dist+maxDist) awayDist = dist+maxDist; if (awayDist < dist-maxDist) awayDist = dist-maxDist; // Used to determine whether NPCs should sprint/avoid aim moveMult = 1.0; if ((dist <= 512) && enemy.bIsPlayer && (enemy.Weapon != None) && (enemyMaxRange < 180)) moveMult = CloseCombatMult; if (bAvoidAim && !enemy.bIgnore && (FRand() <= AvoidAccuracy*moveMult)) { if ((awayDist < enemyMaxRange+maxDist+50) && (awayDist < 800) && (Enemy.Weapon != None)) { if (dist > 0) angle = int(atan(CollisionRadius*2.0/dist)*32768/Pi); else angle = 16384; if ((hAngle >= -angle) && (hAngle <= angle)) { if (hAngle < 0) acrossDist = (-angle-hAngle)-128; else acrossDist = (angle-hAngle)+128; if (Rand(20) == 0) acrossDist = -acrossDist; } } } // projList is implicitly initialized to null... bUseProjVector = false; if (bAvoidHarm && (FRand() <= HarmAccuracy)) { if (GetProjectileList(projList, Location) > 0) { if (IsLocationDangerous(projList, Location)) { projVector = ComputeAwayVector(projList); bUseProjVector = true; } } } reloadMult = 1.0; if (IsWeaponReloading() && Enemy.bIsPlayer) reloadMult = 0.5; bUseSprint = false; if (!bUseProjVector && bSprint && bCanStrafe && !enemy.bIgnore && (FRand() <= SprintRate*0.5*moveMult*reloadMult)) { if (bOuterValid || (innerRange[1] > 100)) // sprint on long-range weapons only { sprintRot = Rotator(enemy.Location - Location); if (Rand(2) == 1) sprintRot.Yaw += 16384; else sprintRot.Yaw += 49152; sprintRot = RandomBiasedRotation(sprintRot.Yaw, 0.5, 0, 0); sprintRot.Pitch = 0; sprintVect = Vector(sprintRot)*GroundSpeed*(FRand()+0.5); bUseSprint = true; } } if ((acrossDist != 0) || (awayDist != dist) || bUseProjVector || bUseSprint) { if (Rand(40) != 0) { if ((destType == DEST_Failure) && bUseProjVector) { tryVector = projVector + Location; if (TryLocation(tryVector, CollisionRadius+16)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && (acrossDist != 0) && (awayDist != dist)) { tryVector = Vector(relativeRotation+(rot(0, 1, 0)*acrossDist))*awayDist + enemy.Location; if (TryLocation(tryVector, CollisionRadius+16, , projList)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && (awayDist != dist)) { tryVector = Vector(relativeRotation)*awayDist + enemy.Location; if (TryLocation(tryVector, CollisionRadius+16, , projList)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && (acrossDist != 0)) { tryVector = Vector(relativeRotation+(rot(0, 1, 0)*acrossDist))*dist + enemy.Location; if (TryLocation(tryVector, CollisionRadius+16, , projList)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && bUseSprint) { tryVector = sprintVect + Location; if (TryLocation(tryVector, CollisionRadius+16)) destType = DEST_NewLocation; } } if (destType == DEST_Failure) { if ((moveMult >= 0.5) || (FRand() <= moveMult)) { if (AIPickRandomDestination(CollisionRadius+16, maxDist, relativeRotation.Yaw+32768, 0.6, -relativeRotation.Pitch, 0.6, 2, 0.9, tryVector)) if (!bDefendHome || IsNearHome(tryVector)) if (!bAvoidHarm || !IsLocationDangerous(projList, tryVector)) destType = DEST_NewLocation; } else destType = DEST_SameLocation; } if (destType != DEST_Failure) newPosition = tryVector; } else destType = DEST_SameLocation; return destType; } // ---------------------------------------------------------------------- // SetAttackAngle() // // Sets the angle from which an asynchronous attack will occur // (hack needed for DeusExWeapon) // ---------------------------------------------------------------------- function SetAttackAngle() { local bool bCanShoot; bCanShoot = false; if (Enemy != None) if (AICanShoot(Enemy, true, false, 0.025)) bCanShoot = true; if (!bCanShoot) ViewRotation = Rotation; } // ---------------------------------------------------------------------- // AdjustAim() // // Adjust the aim at target // ---------------------------------------------------------------------- function rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget) { local rotator FireRotation; local vector FireSpot; local actor HitActor; local vector HitLocation, HitNormal; local vector vectorArray[3]; local vector tempVector; local int i; local int swap; local Rotator rot; local bool bIsThrown; local DeusExMover dxMover; local actor Target; // evil fix -- STM bIsThrown = IsThrownWeapon(DeusExWeapon(Weapon)); // took this line out for evil fix... // if ( Target == None ) Target = Enemy; if ( Target == None ) return Rotation; if ( !Target.IsA('Pawn') ) return rotator(Target.Location - Location); FireSpot = Target.Location; if (leadTarget && !IsA('Gray') && FRand() < 0.7 && (projSpeed > 0)) //CyberP: grays never target lead { if (bIsThrown) { // compute target's position 1.5 seconds in the future FireSpot = target.Location + (target.Velocity*1.5); } else { //FireSpot += (Target.Velocity * VSize(Target.Location - ProjStart)/projSpeed); ComputeTargetLead(Pawn(Target), ProjStart, projSpeed, 20.0, FireSpot); } } else //CyberP: don't always target lead for variation { FireSpot = target.Location; } if (bIsThrown) { vectorArray[0] = FireSpot - vect(0,0,1)*(Target.CollisionHeight-5); // floor vectorArray[1] = vectorArray[0] + Vector(rot(0,1,0)*Rand(65536))*CollisionRadius*1.2; vectorArray[2] = vectorArray[0] + Vector(rot(0,1,0)*Rand(65536))*CollisionRadius*1.2; for (i=0; i<3; i++) { if (AISafeToThrow(vectorArray[i], ProjStart, 0.025)) break; } if (i < 3) { FireSpot = vectorArray[i]; FireRotation = ViewRotation; } else FireRotation = Rotator(FireSpot - ProjStart); } else { dxMover = DeusExMover(Target.Base); if ((dxMover != None) && dxMover.bBreakable) { tempVector = Normal((Location-Target.Location)*vect(1,1,0))*(Target.CollisionRadius*1.01) - vect(0,0,1)*(Target.CollisionHeight*1.01); vectorArray[0] = FireSpot + tempVector; } else if (bAimForHead) vectorArray[0] = FireSpot + vect(0,0,1)*(Target.CollisionHeight*0.85); // head else vectorArray[0] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight); vectorArray[1] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight); vectorArray[2] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight); for (i=0; i<3; i++) { if (AISafeToShoot(HitActor, vectorArray[i], ProjStart)) break; } if (i < 3) FireSpot = vectorArray[i]; FireRotation = Rotator(FireSpot - ProjStart); } if (warnTarget && Pawn(Target) != None) Pawn(Target).WarnTarget(self, projSpeed, vector(FireRotation)); FireRotation.Yaw = FireRotation.Yaw & 65535; if ( (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) > 8192) && (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) < 57343) ) { if ( (FireRotation.Yaw > Rotation.Yaw + 32768) || ((FireRotation.Yaw < Rotation.Yaw) && (FireRotation.Yaw > Rotation.Yaw - 32768)) ) FireRotation.Yaw = Rotation.Yaw - 8192; else FireRotation.Yaw = Rotation.Yaw + 8192; } viewRotation = FireRotation; return FireRotation; } // ---------------------------------------------------------------------- // IsThrownWeapon() // ---------------------------------------------------------------------- function bool IsThrownWeapon(DeusExWeapon testWeapon) { local Class<ThrownProjectile> throwClass; local bool bIsThrown; bIsThrown = false; if (testWeapon != None) { if (!testWeapon.bInstantHit) { throwClass = class<ThrownProjectile>(testWeapon.ProjectileClass); if (throwClass != None) bIsThrown = true; } } return bIsThrown; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CALLBACKS AND OVERRIDDEN FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // Tick() // ---------------------------------------------------------------------- function Tick(float deltaTime) { local float dropPeriod; local float adjustedRate; local DeusExPlayer player; local name stateName; local vector loc; local bool bDoLowPriority; local bool bCheckOther; local bool bCheckPlayer; local float ModDistance; player = DeusExPlayer(GetPlayerPawn()); bDoLowPriority = true; bCheckPlayer = true; bCheckOther = true; if (bTickVisibleOnly) { if (DistanceFromPlayer > 1200) bDoLowPriority = false; if (player !=None && player.CombatDifficulty > 2) //CyberP: realistic & hardcore have realistic vision ModDistance=4096.0; else ModDistance=2590.0; if (DistanceFromPlayer > ModDistance) bCheckPlayer = false; if ((DistanceFromPlayer > 600) && (LastRendered() >= 5.0)) bCheckOther = false; } /* if (bDisappear && (InStasis() || (LastRendered() > 5.0))) { Destroy(); return; } if (PrePivotTime > 0) { if (deltaTime < PrePivotTime) { PrePivot = PrePivot + (DesiredPrePivot-PrePivot)*deltaTime/PrePivotTime; PrePivotTime -= deltaTime; } else { PrePivot = DesiredPrePivot; PrePivotTime = 0; } } if (bDoLowPriority) Super.Tick(deltaTime); UpdateAgitation(deltaTime); UpdateFear(deltaTime); AlarmTimer -= deltaTime; if (AlarmTimer < 0) AlarmTimer = 0; if (Weapon != None) WeaponTimer += deltaTime; else if (WeaponTimer != 0) WeaponTimer = 0; if ((ReloadTimer > 0) && (Weapon != None)) ReloadTimer -= deltaTime; else ReloadTimer = 0; if (AvoidWallTimer > 0) { AvoidWallTimer -= deltaTime; if (AvoidWallTimer < 0) AvoidWallTimer = 0; } if (AvoidBumpTimer > 0) { AvoidBumpTimer -= deltaTime; if (AvoidBumpTimer < 0) AvoidBumpTimer = 0; } if (CloakEMPTimer > 0) { CloakEMPTimer -= deltaTime; if (CloakEMPTimer < 0) CloakEMPTimer = 0; } if (TakeHitTimer > 0) { TakeHitTimer -= deltaTime; if (TakeHitTimer < 0) TakeHitTimer = 0; } if (CarcassCheckTimer > 0) { CarcassCheckTimer -= deltaTime; if (CarcassCheckTimer < 0) CarcassCheckTimer = 0; } if (PotentialEnemyTimer > 0) { PotentialEnemyTimer -= deltaTime; if (PotentialEnemyTimer <= 0) { PotentialEnemyTimer = 0; PotentialEnemyAlliance = ''; } } if (BeamCheckTimer > 0) { BeamCheckTimer -= deltaTime; if (BeamCheckTimer < 0) BeamCheckTimer = 0; } if (FutzTimer > 0) { FutzTimer -= deltaTime; if (FutzTimer < 0) FutzTimer = 0; } if (PlayerAgitationTimer > 0) { PlayerAgitationTimer -= deltaTime; if (PlayerAgitationTimer < 0) PlayerAgitationTimer = 0; } if (DistressTimer >= 0) { DistressTimer += deltaTime; if (DistressTimer > FearSustainTime) DistressTimer = -1; } if (bHasCloak) EnableCloak(Health <= CloakThreshold); if (bAdvancedTactics) { if ((Acceleration == vect(0,0,0)) || (Physics != PHYS_Walking) || (TurnDirection == TURNING_None)) { bAdvancedTactics = false; if (TurnDirection != TURNING_None) MoveTimer -= 4.0; ActorAvoiding = None; NextDirection = TURNING_None; TurnDirection = TURNING_None; bClearedObstacle = true; ObstacleTimer = 0; } } if (bOnFire) { burnTimer += deltaTime; if (burnTimer >= BurnPeriod) ExtinguishFire(); } if (bDoLowPriority) { if ((bleedRate > 0) && bCanBleed) { adjustedRate = (1.0-FClamp(bleedRate, 0.0, 1.0))*1.0+0.1; // max 10 drops per second dropPeriod = adjustedRate / FClamp(VSize(Velocity)/512.0, 0.05, 1.0); dropCounter += deltaTime; while (dropCounter >= dropPeriod) { SpurtBlood(); dropCounter -= dropPeriod; } bleedRate -= deltaTime/clotPeriod; } if (bleedRate <= 0) { dropCounter = 0; bleedRate = 0; } } */ if (bStandInterpolation) UpdateStanding(deltaTime); // this is UGLY! if (bOnFire && (health > 0)) { stateName = GetStateName(); if ((stateName != 'Burning') && (stateName != 'TakingHit') && (stateName != 'RubbingEyes')) GotoState('Burning'); } else { if (bDoLowPriority) { // Don't allow radius-based convos to interupt other conversations! if ((player != None) && (GetStateName() != 'Conversation') && (GetStateName() != 'FirstPersonConversation')) player.StartConversation(Self, IM_Radius); } if (CheckEnemyPresence(deltaTime, bCheckPlayer, bCheckOther)) HandleEnemy(); else { CheckBeamPresence(deltaTime); if (bDoLowPriority || LastRendered() < 5.0) CheckCarcassPresence(deltaTime); // hacky -- may change state! } } // Randomly spawn an air bubble every 0.2 seconds if we're underwater if (HeadRegion.Zone.bWaterZone && bSpawnBubbles && bDoLowPriority) { swimBubbleTimer += deltaTime; if (swimBubbleTimer >= 0.2) { swimBubbleTimer = 0; if (FRand() < 0.4) { loc = Location + VRand() * 4; loc.Z += CollisionHeight * 0.9; Spawn(class'AirBubble', Self,, loc); } } } // Handle poison damage UpdatePoison(deltaTime); } // ---------------------------------------------------------------------- // SpurtBlood() // ---------------------------------------------------------------------- function SpurtBlood() { local vector bloodVector; bloodVector = vect(0,0,1)*CollisionHeight*0.5; // so folks don't bleed from the crotch spawn(Class'BloodDrop',,,bloodVector+Location); } // ---------------------------------------------------------------------- // TakeDamage() // ---------------------------------------------------------------------- function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, true); } // ---------------------------------------------------------------------- // Timer() // ---------------------------------------------------------------------- function Timer() { if (bCloakOn) //CyberP: for new cloaking effect. { if (IsA('SecurityBot3')) SetSkinStyle(STY_Translucent, Texture'HDTPWeaponCrowbarTex2', 0.4); else SetSkinStyle(STY_Translucent, Texture'HDTPWeaponCrowbarTex2', 0.15); LightRadius = 0; AmbientGlow = 0; } else { UpdateFire(); } } // ---------------------------------------------------------------------- // ZoneChange() // ---------------------------------------------------------------------- function ZoneChange(ZoneInfo newZone) { local vector jumpDir; if (!bInWorld) return; if (newZone.bWaterZone) { EnableShadow(false); if (IsA('Doberman')) //CyberP: Doberman have no swim animations, so kill them. TakeDamage(100,self,vect(0,0,0),vect(0,0,0),'Shot'); if (!bCanSwim) MoveTimer = -1.0; else if (Physics != PHYS_Swimming) { if (bOnFire) ExtinguishFire(); PlaySwimming(); setPhysics(PHYS_Swimming); } swimBubbleTimer = 0; } else if (Physics == PHYS_Swimming) { EnableShadow(true); if ( bCanFly ) SetPhysics(PHYS_Flying); else { SetPhysics(PHYS_Falling); if ( bCanWalk && (Abs(Acceleration.X) + Abs(Acceleration.Y) > 0) && CheckWaterJump(jumpDir) ) JumpOutOfWater(jumpDir); } } } // ---------------------------------------------------------------------- // BaseChange() // ---------------------------------------------------------------------- singular function BaseChange() { Super.BaseChange(); if (bSitting && !IsSeatValid(SeatActor)) { StandUp(); if (GetStateName() == 'Sitting') GotoState('Sitting', 'Begin'); } } // ---------------------------------------------------------------------- // PainTimer() // ---------------------------------------------------------------------- event PainTimer() { local float depth; local PointRegion painRegion; if ((Health <= 0) || (Level.NetMode == NM_Client)) return; painRegion = HeadRegion; if (!painRegion.Zone.bPainZone || (painRegion.Zone.DamageType == ReducedDamageType)) painRegion = Region; if (!painRegion.Zone.bPainZone || (painRegion.Zone.DamageType == ReducedDamageType)) painRegion = FootRegion; if (painRegion.Zone.bPainZone && (painRegion.Zone.DamageType != ReducedDamageType)) { depth = 0; if (FootRegion.Zone.bPainZone) depth += 0.3; if (Region.Zone.bPainZone) depth += 0.3; if (HeadRegion.Zone.bPainZone) depth += 0.4; if (painRegion.Zone.DamagePerSec > 0) TakeDamage(int(float(painRegion.Zone.DamagePerSec) * depth), None, Location, vect(0,0,0), painRegion.Zone.DamageType); // took out healing for NPCs -- we don't use healing zones anyway /* else if ( Health < Default.Health ) Health = Min(Default.Health, Health - depth * FootRegion.Zone.DamagePerSec); */ if (Health > 0) PainTime = 1.0; } else if (HeadRegion.Zone.bWaterZone && (UnderWaterTime > 0)) { TakeDamage(5, None, Location, vect(0,0,0), 'Drowned'); if (Health > 0) PainTime = 2.0; } } // ---------------------------------------------------------------------- // CheckWaterJump() // ---------------------------------------------------------------------- function bool CheckWaterJump(out vector WallNormal) { local actor HitActor; local vector HitLocation, HitNormal, checkpoint, start, checkNorm, Extent; if (CarriedDecoration != None) return false; checkpoint = vector(Rotation); checkpoint.Z = 0.0; checkNorm = Normal(checkpoint); checkPoint = Location + CollisionRadius * checkNorm; Extent = CollisionRadius * vect(1,1,0); Extent.Z = CollisionHeight; HitActor = Trace(HitLocation, HitNormal, checkpoint, Location, false, Extent); if ( (HitActor != None) && (Pawn(HitActor) == None) ) { WallNormal = -1 * HitNormal; start = Location; start.Z += 1.1 * MaxStepHeight + CollisionHeight; checkPoint = start + 2 * CollisionRadius * checkNorm; HitActor = Trace(HitLocation, HitNormal, checkpoint, start, true, Extent); if (HitActor == None) return true; } return false; } // ---------------------------------------------------------------------- // SetMovementPhysics() // ---------------------------------------------------------------------- function SetMovementPhysics() { // re-implement SetMovementPhysics() in subclass for flying and swimming creatures if (Physics == PHYS_Falling) return; if (Region.Zone.bWaterZone && bCanSwim) SetPhysics(PHYS_Swimming); else if (Default.Physics == PHYS_None) SetPhysics(PHYS_Walking); else SetPhysics(Default.Physics); } // ---------------------------------------------------------------------- // PreSetMovement() // ---------------------------------------------------------------------- function PreSetMovement() { // Copied from Pawn.uc and overridden so Pawn doesn't erase our walking/swimming/flying physics if (JumpZ > 0) bCanJump = true; // No, no, no!!! /* bCanWalk = true; bCanSwim = false; bCanFly = false; MinHitWall = -0.6; if (Intelligence > BRAINS_Reptile) bCanOpenDoors = true; if (Intelligence == BRAINS_Human) bCanDoSpecial = true; */ } // ---------------------------------------------------------------------- // ChangedWeapon() // ---------------------------------------------------------------------- function ChangedWeapon() { // do nothing } // ---------------------------------------------------------------------- // SwitchToBestWeapon() // ---------------------------------------------------------------------- function bool SwitchToBestWeapon() { local Inventory inv; local DeusExWeapon curWeapon; local float score; local DeusExWeapon dxWeapon; local DeusExWeapon bestWeapon; local float bestScore; local int fallbackLevel; local int curFallbackLevel; local bool bBlockSpecial; local bool bValid; local bool bWinner; local float minRange, accRange; local float range, centerRange; local float cutoffRange; local float enemyRange; local float minEnemy, accEnemy, maxEnemy; local ScriptedPawn enemyPawn; local Robot enemyRobot; local DeusExPlayer enemyPlayer; local float enemyRadius; local bool bEnemySet; local int loopCount, i; // hack - check for infinite inventory local Inventory loopInv; // hack - check for infinite inventory if (ShouldDropWeapon()) { DropWeapon(); return false; } bBlockSpecial = false; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) { if (dxWeapon.AITimeLimit > 0) { if (SpecialTimer <= 0) { bBlockSpecial = true; FireTimer = dxWeapon.AIFireDelay; } } } bestWeapon = None; bestScore = 0; fallbackLevel = 0; inv = Inventory; bEnemySet = false; minEnemy = 0; accEnemy = 0; enemyRange = 400; // default enemyRadius = 0; enemyPawn = None; enemyRobot = None; if (Enemy != None) { bEnemySet = true; enemyRange = VSize(Enemy.Location - Location); enemyRadius = Enemy.CollisionRadius; if (DeusExWeapon(Enemy.Weapon) != None) DeusExWeapon(Enemy.Weapon).GetWeaponRanges(minEnemy, accEnemy, maxEnemy); enemyPawn = ScriptedPawn(Enemy); enemyRobot = Robot(Enemy); enemyPlayer = DeusExPlayer(Enemy); } loopCount = 0; while (inv != None) { // THIS IS A MAJOR HACK!!! loopCount++; if (loopCount == 9999) { log("********** RUNAWAY LOOP IN SWITCHTOBESTWEAPON ("$self$") **********"); loopInv = Inventory; i = 0; while (loopInv != None) { i++; if (i > 300) break; log(" Inventory "$i$" - "$loopInv); loopInv = loopInv.Inventory; } } curWeapon = DeusExWeapon(inv); if (curWeapon != None) { bValid = true; if (curWeapon.ReloadCount > 0) { if (curWeapon.AmmoType == None) bValid = false; else if (curWeapon.AmmoType.AmmoAmount < 1) bValid = false; } // Ensure we can actually use this weapon here if (bValid) { // lifted from DeusExWeapon... if ((curWeapon.EnviroEffective == ENVEFF_Air) || (curWeapon.EnviroEffective == ENVEFF_Vacuum) || (curWeapon.EnviroEffective == ENVEFF_AirVacuum)) if (curWeapon.Region.Zone.bWaterZone) bValid = false; } if (bValid) { GetWeaponBestRange(curWeapon, minRange, accRange); cutoffRange = minRange+(CollisionRadius+enemyRadius); range = (accRange - minRange) * 0.5; centerRange = minRange + range; if (range < 50) range = 50; if (enemyRange < centerRange) score = (centerRange - enemyRange)/range; else score = (enemyRange - centerRange)/range; if ((minRange >= minEnemy) && (accRange <= accEnemy)) score += 0.5; // arbitrary if ((cutoffRange >= enemyRange-CollisionRadius) && (cutoffRange >= 256)) // do not use long-range weapons on short-range targets score += 10000; curFallbackLevel = 3; if (curWeapon.bFallbackWeapon && !bUseFallbackWeapons) curFallbackLevel = 2; if (!bEnemySet && !curWeapon.bUseAsDrawnWeapon) curFallbackLevel = 1; if ((curWeapon.AIFireDelay > 0) && (FireTimer > 0)) curFallbackLevel = 0; if (bBlockSpecial && (curWeapon.AITimeLimit > 0) && (SpecialTimer <= 0)) curFallbackLevel = 0; // Adjust score based on opponent and damage type. // All damage types are listed here, even the ones that aren't used by weapons... :) // (hacky...) switch (curWeapon.WeaponDamageType()) { case 'Exploded': // Massive explosions are always good score -= 0.2; break; case 'Stunned': if (enemyPawn != None) { if (enemyPawn.bStunned) score += 1000; else score -= 1.5; } if (enemyPlayer != None) score += 10; break; case 'TearGas': if (enemyPawn != None) { if (enemyPawn.bStunned) //score += 1000; bValid = false; else score -= 5.0; } if (enemyRobot != None) //score += 10000; bValid = false; break; case 'HalonGas': if (enemyPawn != None) { if (enemyPawn.bStunned) //score += 1000; bValid = false; else if (enemyPawn.bOnFire) //score += 10000; bValid = false; else score -= 3.0; } if (enemyRobot != None) //score += 10000; bValid = false; break; case 'PoisonGas': case 'Poison': case 'PoisonEffect': case 'Radiation': if (enemyRobot != None) //score += 10000; bValid = false; break; case 'Flamed': if (enemyRobot != None && IsA('Robot')) //CyberP: bots don't use flamethrower on other bots score += 2000.0; break; case 'Burned': case 'Shot': if (enemyRobot != None) score += 0.5; else if (IsA('Greasel') && enemyRange < 320) score -= 2000.0; break; case 'Sabot': if (enemyRobot != None) score -= 0.5; break; case 'EMP': case 'NanoVirus': if (enemyRobot != None) score -= 10.0; else if (enemyPlayer != None) score -= 10.0; //CyberP: was 10.0 else //score += 10000; bValid = false; break; case 'Drowned': default: break; } // Special case for current weapon if ((curWeapon == Weapon) && (WeaponTimer < 10.0)) { // If we last changed weapons less than five seconds ago, // keep this weapon if (WeaponTimer < 5.0) score = -10; // If between five and ten seconds, use a sliding scale else score -= (10.0 - WeaponTimer)/5.0; } // Throw a little randomness into the computation... else { score += FRand()*0.1 - 0.05; if (score < 0) score = 0; } if (bValid) { // ugly if (bestWeapon == None) bWinner = true; else if (curFallbackLevel > fallbackLevel) bWinner = true; else if (curFallbackLevel < fallbackLevel) bWinner = false; else if (bestScore > score) bWinner = true; else bWinner = false; if (bWinner) { bestScore = score; bestWeapon = curWeapon; fallbackLevel = curFallbackLevel; } } } } inv = inv.Inventory; } // If we're changing weapons, reset the weapon timers if (Weapon != bestWeapon) { if (!bEnemySet) WeaponTimer = 10; // hack else WeaponTimer = 0; if (bestWeapon != None) if (bestWeapon.AITimeLimit > 0) SpecialTimer = bestWeapon.AITimeLimit; ReloadTimer = 0; } SetWeapon(bestWeapon); return false; } // ---------------------------------------------------------------------- // LoopBaseConvoAnim() // ---------------------------------------------------------------------- function LoopBaseConvoAnim() { // Special case for sitting if (bSitting) { if (!IsAnimating()) PlaySitting(); } // Special case for dancing else if (bDancing) { if (!IsAnimating()) PlayDancing(); } // Otherwise, just do the usual shit else Super.LoopBaseConvoAnim(); } // ---------------------------------------------------------------------- // BackOff() // ---------------------------------------------------------------------- function BackOff() { SetNextState(GetStateName(), 'ContinueFromDoor'); // MASSIVE hackage SetState('BackingOff'); } // ---------------------------------------------------------------------- // CheckDestLoc() // ---------------------------------------------------------------------- function CheckDestLoc(vector newDestLoc, optional bool bPathnode) { if (VSize(newDestLoc-LastDestLoc) <= 16) // too close DestAttempts++; else if (!IsPointInCylinder(self, newDestLoc)) DestAttempts++; else if (bPathnode && (VSize(newDestLoc-LastDestPoint) <= 16)) // too close DestAttempts++; else DestAttempts = 0; LastDestLoc = newDestLoc; if (bPathnode && (DestAttempts == 0)) LastDestPoint = newDestLoc; if (bEnableCheckDest && (DestAttempts >= 4)) //CyberP: hmm... BackOff(); } // ---------------------------------------------------------------------- // ResetDestLoc() // ---------------------------------------------------------------------- function ResetDestLoc() { DestAttempts = 0; LastDestLoc = vect(9999,9999,9999); // hack LastDestPoint = LastDestLoc; } // ---------------------------------------------------------------------- // EnableCheckDestLoc() // ---------------------------------------------------------------------- function EnableCheckDestLoc(bool bEnable) { // if (bEnableCheckDest && !bEnable) ResetDestLoc(); bEnableCheckDest = bEnable; } // ---------------------------------------------------------------------- // HandleTurn() // ---------------------------------------------------------------------- function bool HandleTurn(actor Other) { local bool bHandle; local bool bHackState; local DeusExDecoration dxDecoration; local ScriptedPawn scrPawn; // THIS ENTIRE SECTION IS A MASSIVE HACK TO GET AROUND PATHFINDING PROBLEMS // WHEN AN OBSTACLE COMPLETELY BLOCKS AN NPC'S PATH... bHandle = true; bHackState = false; if (bEnableCheckDest) { if (DestAttempts >= 2) { dxDecoration = DeusExDecoration(Other); scrPawn = ScriptedPawn(Other); if (dxDecoration != None) { if (!dxDecoration.bInvincible && !dxDecoration.bExplosive) { dxDecoration.HitPoints = 0; dxDecoration.TakeDamage(1, self, dxDecoration.Location, vect(0,0,0), 'Kick'); bHandle = false; } else if (DestAttempts >= 3) { bHackState = true; bHandle = false; } } else if (scrPawn != None) { if (DestAttempts >= 3) { if (scrPawn.GetStateName() != 'BackingOff') { bHackState = true; bHandle = false; } } } } if (bHackState) BackOff(); } return (bHandle); } // ---------------------------------------------------------------------- // Bump() // ---------------------------------------------------------------------- function Bump(actor Other) { local Rotator rot1, rot2; local int yaw; local ScriptedPawn avoidPawn; local DeusExPlayer dxPlayer; local bool bTurn; // Handle futzing and projectiles if (Other.Physics == PHYS_Falling) { if (DeusExProjectile(Other) != None) ReactToProjectiles(Other); else { dxPlayer = DeusExPlayer(Other.Instigator); if ((Other != dxPlayer) && (dxPlayer != None)) ReactToFutz(); } } // Have we walked into another (non-level) actor? bTurn = false; if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)) && bWalkAround && (Other != Level) && !Other.IsA('Mover')) if ((TurnDirection == TURNING_None) || (AvoidBumpTimer <= 0) && (Other.CollisionHeight > MaxStepHeight*0.75)) //CyberP: also check for small objects, which we ignore if (HandleTurn(Other)) bTurn = true; // Turn away from the actor if (bTurn) { // If we're not already turning, start if (TurnDirection == TURNING_None) { // Give ourselves a little extra time MoveTimer += 4.0; rot1 = Rotator(Other.Location-Location); // direction of object being bumped rot2 = Rotator(Acceleration); // direction we wish to go yaw = (rot2.Yaw - rot1.Yaw) & 65535; if (yaw > 32767) yaw -= 65536; // Depending on the angle we bump the actor, turn left or right if (yaw < 0) { TurnDirection = TURNING_Left; NextDirection = TURNING_Right; } else { TurnDirection = TURNING_Right; NextDirection = TURNING_Left; } bClearedObstacle = false; // Enable AlterDestination() bAdvancedTactics = true; } // Ignore multiple bumps in a row // BOOGER! Ignore same bump actor? if (AvoidBumpTimer <= 0) { AvoidBumpTimer = 0.2; ActorAvoiding = Other; bClearedObstacle = false; avoidPawn = ScriptedPawn(ActorAvoiding); // Avoid pairing off if (avoidPawn != None) { if ((avoidPawn.Acceleration != vect(0,0,0)) && (avoidPawn.Physics == PHYS_Walking) && (avoidPawn.TurnDirection != TURNING_None) && (avoidPawn.ActorAvoiding == self)) { if ((avoidPawn.TurnDirection == TURNING_Left) && (TurnDirection == TURNING_Right)) { TurnDirection = TURNING_Left; if (NextDirection != TURNING_None) NextDirection = TURNING_Right; } else if ((avoidPawn.TurnDirection == TURNING_Right) && (TurnDirection == TURNING_Left)) { TurnDirection = TURNING_Right; if (NextDirection != TURNING_None) NextDirection = TURNING_Left; } } } } } } // ---------------------------------------------------------------------- // HitWall() // ---------------------------------------------------------------------- function HitWall(vector HitLocation, Actor hitActor) { local ScriptedPawn avoidPawn; // We only care about HitWall as it pertains to level geometry if (hitActor != Level) return; // Are we walking? if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)) && bWalkAround && (AvoidWallTimer <= 0)) { // Are we turning? if (TurnDirection != TURNING_None) { AvoidWallTimer = 1.0; // About face TurnDirection = NextDirection; NextDirection = TURNING_None; bClearedObstacle = false; // Avoid pairing off avoidPawn = ScriptedPawn(ActorAvoiding); if (avoidPawn != None) { if ((avoidPawn.Acceleration != vect(0,0,0)) && (avoidPawn.Physics == PHYS_Walking) && (avoidPawn.TurnDirection != TURNING_None) && (avoidPawn.ActorAvoiding == self)) { if ((avoidPawn.TurnDirection == TURNING_Left) && (TurnDirection == TURNING_Right)) TurnDirection = TURNING_None; else if ((avoidPawn.TurnDirection == TURNING_Right) && (TurnDirection == TURNING_Left)) TurnDirection = TURNING_None; } } // Stopped turning? Shut down if (TurnDirection == TURNING_None) { ActorAvoiding = None; bAdvancedTactics = false; MoveTimer -= 4.0; ObstacleTimer = 0; } } } } // ---------------------------------------------------------------------- // AlterDestination() // ---------------------------------------------------------------------- function AlterDestination() { local Rotator dir; local int avoidYaw; local int destYaw; local int moveYaw; local int angle; local bool bPointInCylinder; local float dist1, dist2; local bool bAround; local vector tempVect; local ETurning oldTurnDir; oldTurnDir = TurnDirection; // Sanity check -- are we done walking around the actor? if (TurnDirection != TURNING_None) { if (!bWalkAround) TurnDirection = TURNING_None; else if (bClearedObstacle) TurnDirection = TURNING_None; else if (ActorAvoiding == None) TurnDirection = TURNING_None; else if (ActorAvoiding.bDeleteMe) TurnDirection = TURNING_None; else if (!IsPointInCylinder(ActorAvoiding, Location, CollisionRadius*2, CollisionHeight*2)) TurnDirection = TURNING_None; } // Are we still turning? if (TurnDirection != TURNING_None) { bAround = false; // Is our destination point inside the actor we're walking around? bPointInCylinder = IsPointInCylinder(ActorAvoiding, Destination, CollisionRadius-8, CollisionHeight-8); if (bPointInCylinder) { dist1 = VSize((Location - ActorAvoiding.Location)*vect(1,1,0)); dist2 = VSize((Location - Destination)*vect(1,1,0)); // Are we on the right side of the actor? if (dist1 > dist2) { // Just make a beeline, if possible tempVect = Destination - ActorAvoiding.Location; tempVect.Z = 0; tempVect = Normal(tempVect) * (ActorAvoiding.CollisionRadius + CollisionRadius); if (tempVect == vect(0,0,0)) Destination = Location; else { tempVect += ActorAvoiding.Location; tempVect.Z = Destination.Z; Destination = tempVect; } } else bAround = true; } else bAround = true; // We have a valid destination -- continue to walk around if (bAround) { // Determine the destination-self-obstacle angle dir = Rotator(ActorAvoiding.Location-Location); avoidYaw = dir.Yaw; dir = Rotator(Destination-Location); destYaw = dir.Yaw; if (TurnDirection == TURNING_Left) angle = (avoidYaw - destYaw) & 65535; else angle = (destYaw - avoidYaw) & 65535; if (angle < 0) angle += 65536; // If the angle is between 90 and 180 degrees, we've cleared the obstacle if (bPointInCylinder || (angle < 16384) || (angle > 32768)) // haven't cleared the actor yet { if (TurnDirection == TURNING_Left) moveYaw = avoidYaw - 16384; else moveYaw = avoidYaw + 16384; Destination = Location + Vector(rot(0,1,0)*moveYaw)*400; } else // cleared the actor -- move on TurnDirection = TURNING_None; } } if (TurnDirection == TURNING_None) { if (ObstacleTimer > 0) { TurnDirection = oldTurnDir; bClearedObstacle = true; } } else ObstacleTimer = 1.5; // Reset if done turning if (TurnDirection == TURNING_None) { NextDirection = TURNING_None; ActorAvoiding = None; bAdvancedTactics = false; ObstacleTimer = 0; bClearedObstacle = true; if (oldTurnDir != TURNING_None) MoveTimer -= 4.0; } } // ---------------------------------------------------------------------- // Frob() // ---------------------------------------------------------------------- function Frob(Actor Frobber, Inventory frobWith) { Super.Frob(Frobber, frobWith); // Check to see if the Frobber is the player. If so, then // check to see if we need to start a conversation. if (DeusExPlayer(Frobber) != None) { if (DeusExPlayer(Frobber).StartConversation(Self, IM_Frob)) { ConversationActor = Pawn(Frobber); return; } } } // ---------------------------------------------------------------------- // StopBlendAnims() // ---------------------------------------------------------------------- function StopBlendAnims() { AIAddViewRotation = rot(0, 0, 0); Super.StopBlendAnims(); PlayTurnHead(LOOK_Forward, 1.0, 1.0); } // ---------------------------------------------------------------------- // Falling() // ---------------------------------------------------------------------- singular function Falling() { if (bCanFly) { SetPhysics(PHYS_Flying); return; } // SetPhysics(PHYS_Falling); //note - done by default in physics if (health > 0) SetFall(); } // ---------------------------------------------------------------------- // SetFall() // ---------------------------------------------------------------------- function SetFall() { GotoState('FallingState'); } // ---------------------------------------------------------------------- // LongFall() // ---------------------------------------------------------------------- function LongFall() { SetFall(); GotoState('FallingState', 'LongFall'); } // ---------------------------------------------------------------------- // HearNoise() // ---------------------------------------------------------------------- function HearNoise(float Loudness, Actor NoiseMaker) { // Do nothing } // ---------------------------------------------------------------------- // SeePlayer() // ---------------------------------------------------------------------- function SeePlayer(Actor SeenPlayer) { // Do nothing } // ---------------------------------------------------------------------- // Trigger() // ---------------------------------------------------------------------- function Trigger( actor Other, pawn EventInstigator ) { // Do nothing } // ---------------------------------------------------------------------- // Reloading() // ---------------------------------------------------------------------- function Reloading(DeusExWeapon reloadWeapon, float reloadTime) { if (reloadWeapon == Weapon) ReloadTimer = reloadTime; } // ---------------------------------------------------------------------- // DoneReloading() // ---------------------------------------------------------------------- function DoneReloading(DeusExWeapon reloadWeapon) { if (reloadWeapon == Weapon) ReloadTimer = 0; } // ---------------------------------------------------------------------- // IsWeaponReloading() // ---------------------------------------------------------------------- function bool IsWeaponReloading() { return (ReloadTimer >= 0.3); } // ---------------------------------------------------------------------- // HandToHandAttack() // ---------------------------------------------------------------------- function HandToHandAttack() { local DeusExWeapon dxWeapon; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) dxWeapon.OwnerHandToHandAttack(); } // ---------------------------------------------------------------------- // Killed() // ---------------------------------------------------------------------- function Killed(pawn Killer, pawn Other, name damageType) { if ((Enemy == Other) && (Other != None) && !Other.bIsPlayer) Enemy = None; } // ---------------------------------------------------------------------- // Died() // ---------------------------------------------------------------------- function Died(pawn Killer, name damageType, vector HitLocation) { local DeusExPlayer player; local name flagName; // Set a flag when NPCs die so we can possibly check it later player = DeusExPlayer(GetPlayerPawn()); ExtinguishFire(); // set the instigator to be the killer Instigator = Killer; if (player != None) { // Abort any conversation we may have been having with the NPC if (bInConversation) player.AbortConversation(); // Abort any barks we may have been playing if (player.BarkManager != None) player.BarkManager.ScriptedPawnDied(Self); } Super.Died(Killer, damageType, HitLocation); // bStunned is set here if (player != None) { if (bImportant) { flagName = player.rootWindow.StringToName(BindName$"_Dead"); player.flagBase.SetBool(flagName, True); // make sure the flag never expires player.flagBase.SetExpiration(flagName, FLAG_Bool, 0); if (bStunned) { flagName = player.rootWindow.StringToName(BindName$"_Unconscious"); player.flagBase.SetBool(flagName, True); // make sure the flag never expires player.flagBase.SetExpiration(flagName, FLAG_Bool, 0); } } } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // STATES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // state StartUp // // Initial state // ---------------------------------------------------------------------- auto state StartUp { function BeginState() { bInterruptState = true; bCanConverse = false; SetMovementPhysics(); if (Physics == PHYS_Walking) SetPhysics(PHYS_Falling); bStasis = False; SetDistress(false); BlockReactions(); ResetDestLoc(); } function EndState() { bCanConverse = true; bStasis = True; ResetReactions(); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); if (LastRendered() <= 1.0) { PlayWaiting(); InitializePawn(); FollowOrders(); } } Begin: InitializePawn(); Sleep(FRand()+0.2); WaitForLanding(); Start: FollowOrders(); } // ---------------------------------------------------------------------- // state Paralyzed // // Do nothing -- ignore all // (this state lets ViewModel work correctly) // ---------------------------------------------------------------------- state Paralyzed { ignores bump, frob, reacttoinjury; function BeginState() { StandUp(); BlockReactions(true); bCanConverse = False; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { ResetReactions(); bCanConverse = True; } Begin: Acceleration=vect(0,0,0); PlayAnimPivot('Still'); } // ---------------------------------------------------------------------- // state Idle // // Do nothing // ---------------------------------------------------------------------- state Idle { ignores bump, frob, reacttoinjury; function BeginState() { StandUp(); BlockReactions(true); bCanConverse = False; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { ResetReactions(); bCanConverse = True; } Begin: Acceleration=vect(0,0,0); DesiredRotation=Rotation; PlayAnimPivot('Still'); Idle: } // ---------------------------------------------------------------------- // state Conversation // // Just sit here until the conversation is over // ---------------------------------------------------------------------- state Conversation { function Tick(float deltaTime) { Global.Tick(deltaTime); LipSynch(deltaTime); // Keep turning towards the person we're speaking to if (ConversationActor != None) { if (bSitting) { if (SeatActor != None) LookAtActor(ConversationActor, true, true, true, 0, 0.5, SeatActor.Rotation.Yaw+49152, 5461); else LookAtActor(ConversationActor, false, true, true, 0, 0.5); } else LookAtActor(ConversationActor, true, true, true, 0, 0.5); } } function LoopHeadConvoAnim() { } function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate) { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) if (dxPlayer.ConPlay != None) if (dxPlayer.ConPlay.GetForcePlay()) { Global.SetOrders(orderName, newOrderTag, bImmediate); return; } ConvOrders = orderName; ConvOrderTag = newOrderTag; } function FollowOrders(optional bool bDefer) { local name tempConvOrders, tempConvOrderTag; // hack tempConvOrders = ConvOrders; tempConvOrderTag = ConvOrderTag; ResetConvOrders(); // must do this before calling SetOrders(), or recursion will result if (tempConvOrders != '') Global.SetOrders(tempConvOrders, tempConvOrderTag, true); else Global.FollowOrders(bDefer); } function BeginState() { local DeusExPlayer dxPlayer; local bool bBlock; ResetConvOrders(); EnableCheckDestLoc(false); bBlock = True; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) if (dxPlayer.ConPlay != None) if (dxPlayer.ConPlay.CanInterrupt()) bBlock = False; bInterruptState = True; if (bBlock) { bCanConverse = False; MakePawnIgnored(true); BlockReactions(true); } else { bCanConverse = True; MakePawnIgnored(true); BlockReactions(false); } // Check if the current state is "WaitingFor", "RunningTo" or "GoingTo", in which case // we want the orders to be 'Standing' after the conversation is over. UNLESS the // ScriptedPawn was going somewhere else (OrderTag != '') if (((Orders == 'WaitingFor') || (Orders == 'RunningTo') || (Orders == 'GoingTo')) && (OrderTag == '')) SetOrders('Standing'); bConversationEndedNormally = False; bInConversation = True; bStasis = False; SetDistress(false); SeekPawn = None; } function EndState() { local DeusExPlayer player; local bool bForcePlay; bForcePlay = false; player = DeusExPlayer(GetPlayerPawn()); if (player != None) if (player.conPlay != None) bForcePlay = player.conPlay.GetForcePlay(); bConvEndState = true; if (!bForcePlay && (bConversationEndedNormally != True)) player.AbortConversation(); bConvEndState = false; ResetConvOrders(); StopBlendAnims(); bInterruptState = true; bCanConverse = True; MakePawnIgnored(false); ResetReactions(); bStasis = True; ConversationActor = None; } Begin: Acceleration = vect(0,0,0); DesiredRotation.Pitch = 0; if (!bSitting && !bDancing) PlayWaiting(); // we are now idle } // ---------------------------------------------------------------------- // state FirstPersonConversation // // Just sit here until the conversation is over // ---------------------------------------------------------------------- state FirstPersonConversation { function Tick(float deltaTime) { Global.Tick(deltaTime); LipSynch(deltaTime); // Keep turning towards the person we're speaking to if (ConversationActor != None) { if (bSitting) { if (SeatActor != None) LookAtActor(ConversationActor, true, true, true, 0, 1.0, SeatActor.Rotation.Yaw+49152, 5461); else LookAtActor(ConversationActor, false, true, true, 0, 1.0); } else LookAtActor(ConversationActor, true, true, true, 0, 1.0); } } function LoopHeadConvoAnim() { } function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate) { ConvOrders = orderName; ConvOrderTag = newOrderTag; } function FollowOrders(optional bool bDefer) { local name tempConvOrders, tempConvOrderTag; // hack tempConvOrders = ConvOrders; tempConvOrderTag = ConvOrderTag; ResetConvOrders(); // must do this before calling SetOrders(), or recursion will result if (tempConvOrders != '') Global.SetOrders(tempConvOrders, tempConvOrderTag, true); else Global.FollowOrders(bDefer); } function BeginState() { local DeusExPlayer dxPlayer; local bool bBlock; ResetConvOrders(); EnableCheckDestLoc(false); dxPlayer = DeusExPlayer(GetPlayerPawn()); /* bBlock = True; if (dxPlayer != None) if (dxPlayer.ConPlay != None) if (dxPlayer.ConPlay.CanInterrupt()) bBlock = False; */ // 1st-person conversations will no longer block; // left old code in here in case people change their minds :) bBlock = false; bInterruptState = True; if (bBlock) { bCanConverse = False; MakePawnIgnored(true); BlockReactions(true); } else { bCanConverse = True; MakePawnIgnored(false); if ((dxPlayer != None) && (dxPlayer.conPlay != None) && dxPlayer.conPlay.con.IsSpeakingActor(dxPlayer)) SetReactions(false, false, false, false, true, false, false, true, false, false, true, true); else ResetReactions(); } bConversationEndedNormally = False; bInConversation = True; bStasis = False; SetDistress(false); SeekPawn = None; } function EndState() { local DeusExPlayer player; bConvEndState = true; if (bConversationEndedNormally != True) { player = DeusExPlayer(GetPlayerPawn()); player.AbortConversation(); } bConvEndState = false; ResetConvOrders(); StopBlendAnims(); bInterruptState = true; bCanConverse = True; MakePawnIgnored(false); ResetReactions(); bStasis = True; ConversationActor = None; } Begin: Acceleration = vect(0,0,0); DesiredRotation.Pitch = 0; if (!bSitting && !bDancing) PlayWaiting(); // we are now idle } // ---------------------------------------------------------------------- // EnterConversationState() // ---------------------------------------------------------------------- function EnterConversationState(bool bFirstPerson, optional bool bAvoidState) { // First check to see if we're already in a conversation state, // in which case we'll abort immediately if ((GetStateName() == 'Conversation') || (GetStateName() == 'FirstPersonConversation')) return; SetNextState(GetStateName(), 'Begin'); // If bAvoidState is set, then we don't want to jump into the conversaton state // for the ScriptedPawn, because bad things might happen otherwise. if (!bAvoidState) { if (bFirstPerson) GotoState('FirstPersonConversation'); else GotoState('Conversation'); } } // ---------------------------------------------------------------------- // state Standing // // Just kinda stand there and do nothing. // (similar to Wandering, except the pawn doesn't actually move) // ---------------------------------------------------------------------- state Standing { ignores EnemyNotVisible; function SetFall() { StartFalling('Standing', 'ContinueStand'); } function AnimEnd() { PlayWaiting(); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Tick(float deltaSeconds) { animTimer[1] += deltaSeconds; Global.Tick(deltaSeconds); } function BeginState() { StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bStasis = True; StopBlendAnims(); } Begin: WaitForLanding(); if (!bUseHome) Goto('StartStand'); MoveToBase: if (!IsPointInCylinder(self, HomeLoc, 16-CollisionRadius)) { EnableCheckDestLoc(true); while (true) { if (PointReachable(HomeLoc)) { if (ShouldPlayWalk(HomeLoc)) PlayWalking(); MoveTo(HomeLoc, GetWalkingSpeed()); CheckDestLoc(HomeLoc); break; } else { MoveTarget = FindPathTo(HomeLoc); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } EnableCheckDestLoc(false); } TurnTo(Location+HomeRot); StartStand: Acceleration=vect(0,0,0); Goto('Stand'); ContinueFromDoor: Goto('MoveToBase'); Stand: ContinueStand: // nil bStasis = True; PlayWaiting(); if (!bPlayIdle) Goto('DoNothing'); Sleep(FRand()*14+8); Fidget: if (FRand() < 0.5) { PlayIdle(); FinishAnim(); } else { if (FRand() > 0.5) { PlayTurnHead(LOOK_Up, 1.0, 1.0); Sleep(2.0); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); } else if (FRand() > 0.5) { PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.5); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.9); PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.2); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); } else { PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.5); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.9); PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.2); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); } } if (FRand() < 0.3) PlayIdleSound(); Goto('Stand'); DoNothing: // nil } // ---------------------------------------------------------------------- // state Dancing // // Dance in place. // (Most of this code was ripped from Standing) // ---------------------------------------------------------------------- state Dancing { ignores EnemyNotVisible; function SetFall() { StartFalling('Dancing', 'ContinueDance'); } function AnimEnd() { PlayDancing(); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function BeginState() { if (bSitting && !bDancing) StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bStasis = True; StopBlendAnims(); } Begin: WaitForLanding(); if (bDancing) { if (bUseHome) TurnTo(Location+HomeRot); Goto('StartDance'); } if (!bUseHome) Goto('StartDance'); MoveToBase: if (!IsPointInCylinder(self, HomeLoc, 16-CollisionRadius)) { EnableCheckDestLoc(true); while (true) { if (PointReachable(HomeLoc)) { if (ShouldPlayWalk(HomeLoc)) PlayWalking(); MoveTo(HomeLoc, GetWalkingSpeed()); CheckDestLoc(HomeLoc); break; } else { MoveTarget = FindPathTo(HomeLoc); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } EnableCheckDestLoc(false); } TurnTo(Location+HomeRot); StartDance: Acceleration=vect(0,0,0); Goto('Dance'); ContinueFromDoor: Goto('MoveToBase'); Dance: ContinueDance: // nil bDancing = True; PlayDancing(); bStasis = True; if (!bHokeyPokey) Goto('DoNothing'); Spin: Sleep(FRand()*5+5); useRot = DesiredRotation; if (FRand() > 0.5) { TurnTo(Location+1000*vector(useRot+rot(0,16384,0))); TurnTo(Location+1000*vector(useRot+rot(0,32768,0))); TurnTo(Location+1000*vector(useRot+rot(0,49152,0))); } else { TurnTo(Location+1000*vector(useRot+rot(0,49152,0))); TurnTo(Location+1000*vector(useRot+rot(0,32768,0))); TurnTo(Location+1000*vector(useRot+rot(0,16384,0))); } TurnTo(Location+1000*vector(useRot)); Goto('Spin'); DoNothing: // nil } // ---------------------------------------------------------------------- // state Sitting // // Just kinda sit there and do nothing. // (doubles as a shitting state) // ---------------------------------------------------------------------- state Sitting { ignores EnemyNotVisible; function SetFall() { StartFalling('Sitting', 'ContinueSit'); } function AnimEnd() { PlayWaiting(); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; if (!bAcceptBump) NextDirection = TURNING_None; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool HandleTurn(Actor Other) { if (Other == SeatActor) return true; else return Global.HandleTurn(Other); } function Bump(actor bumper) { // If we hit our chair, move to the right place if ((bumper == SeatActor) && bAcceptBump) { bAcceptBump = false; GotoState('Sitting', 'CircleToFront'); } // Handle conversations, if need be else Global.Bump(bumper); } function Tick(float deltaSeconds) { local vector endPos; local vector newPos; local float delta; Global.Tick(deltaSeconds); if (bSitInterpolation && (SeatActor != None)) { endPos = SitPosition(SeatActor, SeatSlot); if ((deltaSeconds < remainingSitTime) && (remainingSitTime > 0)) { delta = deltaSeconds/remainingSitTime; newPos = (endPos-Location)*delta + Location; remainingSitTime -= deltaSeconds; } else { remainingSitTime = 0; bSitInterpolation = false; newPos = endPos; Acceleration = vect(0,0,0); Velocity = vect(0,0,0); SetBase(SeatActor); bSitting = true; } SetLocation(newPos); DesiredRotation = SeatActor.Rotation+Rot(0, -16384, 0); } } function Vector SitPosition(Seat seatActor, int slot) { local float newAssHeight; newAssHeight = GetDefaultCollisionHeight() + BaseAssHeight; newAssHeight = -(CollisionHeight - newAssHeight); return ((seatActor.sitPoint[slot]>>seatActor.Rotation)+seatActor.Location+(vect(0,0,-1)*newAssHeight)); } function vector GetDestinationPosition(Seat seatActor, optional float extraDist) { local Rotator seatRot; local Vector destPos; if (seatActor == None) return (Location); seatRot = seatActor.Rotation + Rot(0, -16384, 0); seatRot.Pitch = 0; destPos = seatActor.Location; destPos += vect(0,0,1)*(CollisionHeight-seatActor.CollisionHeight); destPos += Vector(seatRot)*(seatActor.CollisionRadius+CollisionRadius+extraDist); return (destPos); } function bool IsIntersectingSeat() { local bool bIntersect; local vector testVector; bIntersect = false; if (SeatActor != None) bIntersect = IsOverlapping(SeatActor); return (bIntersect); } function int FindBestSlot(Seat seatActor, out float slotDist) { local int bestSlot; local float dist; local float bestDist; local int i; bestSlot = -1; bestDist = 100; if (!seatActor.Region.Zone.bWaterZone) { for (i=0; i<seatActor.numSitPoints; i++) { if (seatActor.sittingActor[i] == None) { dist = VSize(SitPosition(seatActor, i) - Location); if ((bestSlot < 0) || (bestDist > dist)) { bestDist = dist; bestSlot = i; } } } } slotDist = bestDist; return (bestSlot); } function FindBestSeat() { local Seat curSeat; local Seat bestSeat; local float curDist; local float bestDist; local int curSlot; local int bestSlot; local bool bTry; if (bUseFirstSeatOnly && bSeatHackUsed) { bestSeat = SeatHack; // use the seat hack bestSlot = -1; if (!IsSeatValid(bestSeat)) bestSeat = None; else { if (GetNextWaypoint(bestSeat) == None) bestSeat = None; else { bestSlot = FindBestSlot(bestSeat, curDist); if (bestSlot < 0) bestSeat = None; } } } else { bestSeat = Seat(OrderActor); // try the ordered seat first if (bestSeat != None) { if (!IsSeatValid(OrderActor)) bestSeat = None; else { if (GetNextWaypoint(bestSeat) == None) bestSeat = None; else { bestSlot = FindBestSlot(bestSeat, curDist); if (bestSlot < 0) bestSeat = None; } } } if (bestSeat == None) { bestDist = 10001; bestSlot = -1; foreach RadiusActors(Class'Seat', curSeat, 10000) { if (IsSeatValid(curSeat)) { curSlot = FindBestSlot(curSeat, curDist); if (curSlot >= 0) { if (bestDist > curDist) { if (GetNextWaypoint(curSeat) != None) { bestDist = curDist; bestSeat = curSeat; bestSlot = curSlot; } } } } } } } if (bestSeat != None) { bestSeat.sittingActor[bestSlot] = self; SeatLocation = bestSeat.Location; bSeatLocationValid = true; } else bSeatLocationValid = false; if (bUseFirstSeatOnly && !bSeatHackUsed) { SeatHack = bestSeat; bSeatHackUsed = true; } SeatActor = bestSeat; SeatSlot = bestSlot; } function FollowSeatFallbackOrders() { FindBestSeat(); if (IsSeatValid(SeatActor)) GotoState('Sitting', 'Begin'); else GotoState('Wandering'); } function BeginState() { SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; bAcceptBump = True; if (SeatActor == None) FindBestSeat(); bSitInterpolation = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); if (!bSitting) StandUp(); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bSitInterpolation = false; bStasis = True; } Begin: WaitForLanding(); if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); if (!bSitting) WaitForLanding(); else { TurnTo(Vector(SeatActor.Rotation+Rot(0, -16384, 0))*100+Location); Goto('ContinueSitting'); } MoveToSeat: if (IsIntersectingSeat()) Goto('MoveToPosition'); bAcceptBump = true; while (true) { if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); destLoc = GetDestinationPosition(SeatActor); if (PointReachable(destLoc)) { if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); CheckDestLoc(destLoc); if (IsPointInCylinder(self, GetDestinationPosition(SeatActor), 16, 16)) { bAcceptBump = false; Goto('MoveToPosition'); break; } } else { MoveTarget = GetNextWaypoint(SeatActor); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } CircleToFront: bAcceptBump = false; if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); if (ShouldPlayWalk(GetDestinationPosition(SeatActor, 16))) PlayWalking(); MoveTo(GetDestinationPosition(SeatActor, 16), GetWalkingSpeed()); MoveToPosition: if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); bSitting = true; EnableCollision(false); Acceleration=vect(0,0,0); Sit: Acceleration=vect(0,0,0); Velocity=vect(0,0,0); if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); remainingSitTime = 0.8; PlaySittingDown(); SetBasedPawnSize(CollisionRadius, GetSitHeight()); SetPhysics(PHYS_Flying); StopStanding(); bSitInterpolation = true; while (bSitInterpolation) Sleep(0); FinishAnim(); Goto('ContinueSitting'); ContinueFromDoor: Goto('MoveToSeat'); ContinueSitting: if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); SetBasedPawnSize(CollisionRadius, GetSitHeight()); SetCollision(Default.bCollideActors, Default.bBlockActors, Default.bBlockPlayers); PlaySitting(); bStasis = True; // nil } // ---------------------------------------------------------------------- // state HandlingEnemy // // Fight-or-flight state // ---------------------------------------------------------------------- state HandlingEnemy { function BeginState() { if (Enemy == None) GotoState('Seeking'); else if (RaiseAlarm == RAISEALARM_BeforeAttacking) GotoState('Alerting'); else GotoState('Attacking'); } Begin: } // ---------------------------------------------------------------------- // state Wandering // // Meander from place to place, occasionally gazing into the middle // distance. // ---------------------------------------------------------------------- state Wandering { ignores EnemyNotVisible; function SetFall() { StartFalling('Wandering', 'ContinueWander'); } function Bump(actor bumper) { if (bAcceptBump) { // If we get bumped by another actor while we wait, start wandering again bAcceptBump = False; Disable('AnimEnd'); GotoState('Wandering', 'Wander'); } // Handle conversations, if need be Global.Bump(bumper); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool GoHome() { if (bUseHome && !IsNearHome(Location)) { destLoc = HomeLoc; destPoint = None; if (PointReachable(destLoc)) return true; else { MoveTarget = FindPathTo(destLoc); if (MoveTarget != None) return true; else return false; } } else return false; } function PickDestination() { local WanderCandidates candidates[5]; local int candidateCount; local int maxCandidates; local int maxLastPoints; local WanderPoint curPoint; local Actor wayPoint; local int i; local int openSlot; local float maxDist; local float dist; local float angle; local float magnitude; local int iterations; local bool bSuccess; local Rotator rot; maxCandidates = 4; // must be <= size of candidates[] array maxLastPoints = 2; // must be <= size of lastPoints[] array for (i=0; i<maxCandidates; i++) candidates[i].dist = 100000; candidateCount = 0; // A certain percentage of the time, we want to angle off to a random direction... if ((RandomWandering < 1) && (FRand() > RandomWandering)) { // Fill the candidate table foreach RadiusActors(Class'WanderPoint', curPoint, 3000*wanderlust+1000) // 1000-4000 { // Make sure we haven't been here recently for (i=0; i<maxLastPoints; i++) { if (lastPoints[i] == curPoint) break; } if (i >= maxLastPoints) { // Can we get there from here? wayPoint = GetNextWaypoint(curPoint); if ((wayPoint != None) && !IsNearHome(curPoint.Location)) wayPoint = None; // Yep if (wayPoint != None) { // Find an empty slot for this candidate openSlot = -1; dist = VSize(curPoint.location - location); maxDist = dist; // This candidate will only replace more distant candidates... for (i=0; i<maxCandidates; i++) { if (maxDist < candidates[i].dist) { maxDist = candidates[i].dist; openSlot = i; } } // Put the candidate in the (unsorted) list if (openSlot >= 0) { candidates[openSlot].point = curPoint; candidates[openSlot].waypoint = wayPoint; candidates[openSlot].dist = dist; if (candidateCount < maxCandidates) candidateCount++; } } } } } // Shift our list of recently visited points for (i=maxLastPoints-1; i>0; i--) lastPoints[i] = lastPoints[i-1]; lastPoints[0] = None; // Do we have a list of candidates? if (candidateCount > 0) { // Pick a candidate at random i = Rand(candidateCount); curPoint = candidates[i].point; wayPoint = candidates[i].waypoint; lastPoints[0] = curPoint; MoveTarget = wayPoint; destPoint = curPoint; } // No candidates -- find a random place to go else { MoveTarget = None; destPoint = None; iterations = 6; // try up to 6 different directions while (iterations > 0) { // How far will we go? magnitude = (wanderlust*400+200) * (FRand()*0.2+0.9); // 200-600, +/-10% // Choose our destination, based on whether we have a home base if (!bUseHome) bSuccess = AIPickRandomDestination(100, magnitude, 0, 0, 0, 0, 1, FRand()*0.4+0.35, destLoc); else { if (magnitude > HomeExtent) magnitude = HomeExtent*(FRand()*0.2+0.9); rot = Rotator(HomeLoc-Location); bSuccess = AIPickRandomDestination(50, magnitude, rot.Yaw, 0.25, rot.Pitch, 0.25, 1, FRand()*0.4+0.35, destLoc); } // Success? Break out of the iteration loop if (bSuccess) if (IsNearHome(destLoc)) break; // We failed -- try again iterations--; } // If we got a destination, go there if (iterations <= 0) destLoc = Location; } } function AnimEnd() { PlayWaiting(); } function BeginState() { StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { local int i; bAcceptBump = True; EnableCheckDestLoc(false); // Clear out our list of last visited points for (i=0; i<ArrayCount(lastPoints); i++) lastPoints[i] = None; if (JumpZ > 0) bCanJump = true; } Begin: destPoint = None; GoHome: bAcceptBump = false; WaitForLanding(); if (!GoHome()) Goto('WanderInternal'); MoveHome: EnableCheckDestLoc(true); while (true) { if (PointReachable(destLoc)) { if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); CheckDestLoc(destLoc); break; } else { MoveTarget = FindPathTo(destLoc); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } EnableCheckDestLoc(false); Goto('Pausing'); Wander: WaitForLanding(); WanderInternal: PickDestination(); Moving: // Move from pathnode to pathnode until we get where we're going // (ooooold code -- no longer used) if (destPoint != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); while ((MoveTarget != None) && (MoveTarget != destPoint)) { MoveTarget = FindPathToward(destPoint); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); } } } else if (destLoc != Location) { if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); } else Sleep(0.5); Pausing: Acceleration = vect(0, 0, 0); // Turn in the direction dictated by the WanderPoint, if there is one sleepTime = 6.0; if (WanderPoint(destPoint) != None) { if (WanderPoint(destPoint).gazeItem != None) { TurnToward(WanderPoint(destPoint).gazeItem); sleepTime = WanderPoint(destPoint).gazeDuration; } else if (WanderPoint(destPoint).gazeDirection != vect(0, 0, 0)) TurnTo(Location + WanderPoint(destPoint).gazeDirection); } Enable('AnimEnd'); TweenToWaiting(0.2); bAcceptBump = True; PlayScanningSound(); sleepTime *= (-0.9*restlessness) + 1; Sleep(sleepTime); Disable('AnimEnd'); bAcceptBump = False; FinishAnim(); Goto('Wander'); ContinueWander: ContinueFromDoor: FinishAnim(); PlayWalking(); Goto('Wander'); } // ---------------------------------------------------------------------- // state Leaving // // Just like Patrolling, but make the pawn transient. // ---------------------------------------------------------------------- State Leaving { function BeginState() { bTransient = True; // this pawn will be destroyed when it gets out of range bDisappear = True; GotoState('Patrolling'); } Begin: // shouldn't ever reach this point } // ---------------------------------------------------------------------- // state Patrolling // // Move from point to point in a predescribed pattern. // ---------------------------------------------------------------------- State Patrolling { function SetFall() { StartFalling('Patrolling', 'ContinuePatrol'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function PatrolPoint PickStartPoint() { local NavigationPoint nav; local PatrolPoint curNav; local float curDist; local PatrolPoint closestNav; local float closestDist; nav = Level.NavigationPointList; while (nav != None) { nav.visitedWeight = 0; nav = nav.nextNavigationPoint; } closestNav = None; closestDist = 100000; nav = Level.NavigationPointList; while (nav != None) { curNav = PatrolPoint(nav); if ((curNav != None) && (curNav.Tag == OrderTag)) { while (curNav != None) { if (curNav.visitedWeight != 0) // been here before break; curDist = VSize(Location - curNav.Location); if ((closestNav == None) || (closestDist > curDist)) { closestNav = curNav; closestDist = curDist; } curNav.visitedWeight = 1; curNav = curNav.NextPatrolPoint; } } nav = nav.nextNavigationPoint; } return (closestNav); } function PickDestination() { if (PatrolPoint(destPoint) != None) destPoint = PatrolPoint(destPoint).NextPatrolPoint; else destPoint = PickStartPoint(); if (destPoint == None) // can't go anywhere... GotoState('Standing'); } function BeginState() { StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); SetupWeapon(false); SetDistress(false); bStasis = false; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); Enable('AnimEnd'); bStasis = true; } Begin: destPoint = None; Patrol: //Disable('Bump'); WaitForLanding(); PickDestination(); Moving: // Move from pathnode to pathnode until we get where we're going if (destPoint != None) { if (!IsPointInCylinder(self, destPoint.Location, 16-CollisionRadius)) { EnableCheckDestLoc(true); MoveTarget = FindPathToward(destPoint); while (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); if (MoveTarget == destPoint) break; MoveTarget = FindPathToward(destPoint); } EnableCheckDestLoc(false); } } else Goto('Patrol'); Pausing: if (!bAlwaysPatrol) bStasis = true; Acceleration = vect(0, 0, 0); // Turn in the direction dictated by the WanderPoint, or a random direction if (PatrolPoint(destPoint) != None) { if ((PatrolPoint(destPoint).pausetime > 0) || (PatrolPoint(destPoint).NextPatrolPoint == None)) { if (ShouldPlayTurn(Location + PatrolPoint(destPoint).lookdir)) PlayTurning(); TurnTo(Location + PatrolPoint(destPoint).lookdir); Enable('AnimEnd'); TweenToWaiting(0.2); PlayScanningSound(); //Enable('Bump'); sleepTime = PatrolPoint(destPoint).pausetime * ((-0.9*restlessness) + 1); Sleep(sleepTime); Disable('AnimEnd'); //Disable('Bump'); FinishAnim(); } } Goto('Patrol'); ContinuePatrol: ContinueFromDoor: FinishAnim(); PlayWalking(); Goto('Moving'); } // ---------------------------------------------------------------------- // state Seeking // // Look for enemies in the area // ---------------------------------------------------------------------- State Seeking { function SetFall() { StartFalling('Seeking', 'ContinueSeek'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool GetNextLocation(out vector nextLoc) { local float dist; local rotator rotation; local bool bDone; local float seekDistance; local Actor hitActor; local vector HitLocation, HitNormal; local vector diffVect; local bool bLOS; if (bSeekLocation) { if (SeekType == SEEKTYPE_Guess) seekDistance = (200+FClamp(GroundSpeed*EnemyLastSeen*0.5, 0, 1000)); else seekDistance = 300; } else seekDistance = 60; dist = VSize(Location-destLoc); bDone = false; bLOS = false; if (dist < seekDistance) { bLOS = true; foreach TraceVisibleActors(Class'Actor', hitActor, hitLocation, hitNormal, destLoc, Location+vect(0,0,1)*BaseEyeHeight) { if (hitActor != self) { if (hitActor == Level) bLOS = false; else if (IsPointInCylinder(hitActor, destLoc, 16, 16)) break; else if (hitActor.bBlockSight && !hitActor.bHidden) bLOS = false; } if (!bLOS) break; } } if (!bLOS) { if (PointReachable(destLoc)) { rotation = Rotator(destLoc - Location); if (seekDistance == 0) nextLoc = destLoc; else if (!AIDirectionReachable(destLoc, rotation.Yaw, rotation.Pitch, 0, seekDistance, nextLoc)) bDone = true; if (!bDone && bDefendHome && !IsNearHome(nextLoc)) bDone = true; if (!bDone) // hack, because Unreal's movement code SUCKS { diffVect = nextLoc - Location; if (Physics == PHYS_Walking) diffVect *= vect(1,1,0); if (VSize(diffVect) < 20) bDone = true; else if (IsPointInCylinder(self, nextLoc, 10, 10)) bDone = true; } } else { MoveTarget = FindPathTo(destLoc); if (MoveTarget == None) bDone = true; else if (bDefendHome && !IsNearHome(MoveTarget.Location)) bDone = true; else nextLoc = MoveTarget.Location; } } else bDone = true; return (!bDone); } function bool PickDestination() { local bool bValid; bValid = false; if (/*(EnemyLastSeen <= 25.0) &&*/ (SeekLevel > 0)) { if (bSeekLocation) { bValid = true; destLoc = LastSeenPos; } else { bValid = AIPickRandomDestination(130, 320, 0, 0, 0, 0, 2, 1.0, destLoc); //CyberP: param 2 was 250 if (!bValid) { bValid = true; destLoc = Location + VRand()*50; } else destLoc += vect(0,0,1)*BaseEyeHeight; } } return (bValid); } function NavigationPoint GetOvershootDestination(float randomness, optional float focus) { local NavigationPoint navPoint, bestPoint; local float distance; local float score, bestScore; local int yaw; local rotator rot; local float yawCutoff; if (focus <= 0) focus = 0.6; yawCutoff = int(32768*focus); bestPoint = None; bestScore = 0; foreach ReachablePathnodes(Class'NavigationPoint', navPoint, None, distance) { if (distance < 1) distance = 1; rot = Rotator(navPoint.Location-Location); yaw = rot.Yaw + (16384*randomness); yaw = (yaw-Rotation.Yaw) & 0xFFFF; if (yaw > 32767) yaw -= 65536; yaw = abs(yaw); if (yaw <= yawCutoff) { score = yaw/distance; if ((bestPoint == None) || (score < bestScore)) { bestPoint = navPoint; bestScore = score; } } } return bestPoint; } function Tick(float deltaSeconds) { animTimer[1] += deltaSeconds; Global.Tick(deltaSeconds); UpdateActorVisibility(Enemy, deltaSeconds, 0.5, true); //CyberP: was 1.0 } function HandleLoudNoise(Name event, EAIEventState state, XAIParams params) { local Actor bestActor; local Pawn instigator; local float locDiff; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if ((bestActor != None) && (EnemyLastSeen > 2.0)) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { if (IsValidEnemy(instigator)) { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); destLoc = LastSeenPos; locDiff = Abs(VSize(instigator.Location - Location)); if (bInterruptSeek && locDiff > 416) //CyberP: only turn around when close range GotoState('Seeking', 'GoToLocation'); } } } } } //GMDX: HandleGenericNoise test function HandleGenericNoise(Name event, EAIEventState state, XAIParams params) { local Actor bestActor; local Pawn instigator; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if ((bestActor != None) && (EnemyLastSeen > 2.0)) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); destLoc = LastSeenPos; if (bInterruptSeek) GotoState('Seeking', 'GoToLocation'); } } } } function HandleSighting(Pawn pawnSighted) { if ((EnemyLastSeen > 2.0) && IsValidEnemy(pawnSighted)) { SetSeekLocation(pawnSighted, pawnSighted.Location, SEEKTYPE_Sight); destLoc = LastSeenPos; if (bInterruptSeek) GotoState('Seeking', 'GoToLocation'); } } function checkFlareThrow() { local DeusExPlayer playa; local float dista, visi; local int casted; bCanFlare = False; playa = DeusExPlayer(GetPlayerPawn()); if (playa != none && playa.CombatDifficulty > 0.5) { if (FastTrace(destLoc,Location)) { dista = Abs(VSize(playa.Location - Location)); if (dista < 800 && (Abs(Location.Z-playa.Location.Z) < 160)) //CyberP: make sure we are close and without much height diff. { visi = Playa.AIVisibility(false); casted = (int(visi*100)); if (casted < 15) bCanFlare = True; } } } } function flareThrow() { local flare flared; local vector flareOffset; flareOffset = Location; flareOffset.Z += 50; flareOffset.X += CollisionRadius; flared = Spawn(class'Flare',self,,flareOffset,ViewRotation); if (flared != None) { flared.LightFlare(); flared.Velocity = Vector(Rotation)*1000; flared.Velocity.Z += 100; } } function BeginState() { StandUp(); Disable('AnimEnd'); destLoc = LastSeenPos; SetReactions(true, true, false, true, true, true, true, true, true, false, true, true); bCanConverse = False; bStasis = False; SetupWeapon(true); SetDistress(false); bInterruptState=True; //CyberP: commented out for now //set back EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); Enable('AnimEnd'); ResetReactions(); bCanConverse = True; bStasis = True; StopBlendAnims(); SeekLevel = 0; } Begin: WaitForLanding(); PlayWaiting(); if ((Weapon != None) && bKeepWeaponDrawn && (Weapon.CockingSound != None) && !bSeekPostCombat) PlaySound(Weapon.CockingSound, SLOT_None,,, 1024); Acceleration = vect(0,0,0); if (!PickDestination()) Goto('DoneSeek'); GoToLocation: bInterruptSeek = true; Acceleration = vect(0,0,0); if ((DeusExWeapon(Weapon) != None) && DeusExWeapon(Weapon).CanReload() && !Weapon.IsInState('Reload')) DeusExWeapon(Weapon).ReloadAmmo(); if (bSeekPostCombat) PlayPostAttackSearchingSound(); else if (SeekType == SEEKTYPE_Sound) { PlayPreAttackSearchingSound(); disturbanceCount++; if (FRand() < 0.4 && SeekLevel >= 3) //CyberP: sometimes just turn to the source of the sound instead of running around { SeekLevel = 0; GoTo('TurnToLocation'); } } else if (SeekType == SEEKTYPE_Sight) { if (ReactionLevel > 0.5) PlayPreAttackSightingSound(); disturbanceCount++; } else if ((SeekType == SEEKTYPE_Carcass) && bSeekLocation) PlayCarcassSound(); StopBlendAnims(); if ((SeekType == SEEKTYPE_Sight) && bSeekLocation) Goto('TurnToLocation'); EnableCheckDestLoc(true); if (SeekType == SEEKTYPE_Sound && FRand() < 0.1 && SeekLevel >=3) { while (GetNextLocation(useLoc)) { if (ShouldPlayWalk(useLoc)) PlayWalking(); MoveTo(useLoc, GetWalkingSpeed()); CheckDestLoc(useLoc); } } else { while (GetNextLocation(useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CheckDestLoc(useLoc); } } EnableCheckDestLoc(false); if ((SeekType == SEEKTYPE_Guess) && bSeekLocation) { MoveTarget = GetOvershootDestination(0.5); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); } if (AIPickRandomDestination(CollisionRadius*2, 200+FRand()*200, Rotation.Yaw, 0.75, Rotation.Pitch, 0.75, 2, 0.4, useLoc)) { if (ShouldPlayWalk(useLoc)) { PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); } } } TurnToLocation: Acceleration = vect(0,0,0); PlayTurning(); if ((SeekType == SEEKTYPE_Guess) && bSeekLocation) destLoc = Location + Vector(Rotation+(rot(0,1,0)*(Rand(16384)-8192)))*1000; if (bCanTurnHead) { Sleep(0); // needed to turn head LookAtVector(destLoc, true, false, true); TurnTo(Vector(DesiredRotation)*1000+Location); } else TurnTo(destLoc); bSeekLocation = false; bInterruptSeek = True; //CyberP: was false PlayWaiting(); if (SeekType == SEEKTYPE_Sound && SeekLevel == 0) { Sleep(FRand()*2+3); GoTo('LookAroundAgain'); } /*if (disturbanceCount > 16 && disturbanceCount < 19) { if (RaiseAlarm != RAISEALARM_Never) { RaiseAlarm = RAISEALARM_BeforeAttacking; //CyberP: pawns that witness too many disturbances go for alarm GotoState('Alerting'); } }*/ if (SeekType == SEEKTYPE_Sight) //CyberP: if type sight, sleep for FRand()+1. { if (IsA('Doberman') && FRand() < 0.5) PlaySound(sound'DogAttack2',SLOT_None,,,1024); Sleep(FRand()+1.0); if (FRand() < 0.4) //CyberP: begin { SeekType = SEEKTYPE_Guess; GoTo('GoToLocation'); } //CyberP: end. This makes pawns sometimes seek the location else { if ((IsA('Terrorist') || IsA('MJ12Troop') || IsA('UNATCOTroop')) && !bHasThrownFlare) //CyberP: flare throw checkFlareThrow(); if (bCanFlare) { FinishAnim(); PlayAnimPivot('Attack',,0.1); Sleep(0.3); flareThrow(); bHasThrownFlare = True; bCanFlare = False; FinishAnim(); PlayWaiting(); Sleep(FRand()+0.1); if (FRand() < 0.5) //CyberP: sometimes go over to where you threw the flare too { SeekType = SEEKTYPE_Guess; GoTo('GoToLocation'); } } } } else Sleep(FRand()+0.2); //CyberP: else we don't want much sleeping on the job. :) FindAnotherPlaceAgainAgain: //CyberP: New label for another run around if (FRand() < 0.85) //CyberP: sometimes seek level isn't decremented SeekLevel--; if (PickDestination()) Goto('GoToLocation'); LookAround: //CyberP: look around now takes less time to execute if (bCanTurnHead) { if (FRand() < 0.5) { if (!bSeekLocation) { PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.0); } } else { if (!bSeekLocation) { PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.0); } } PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); StopBlendAnims(); } else { if (!bSeekLocation) Sleep(0.6); //CyberP: was 1.0 } if (SeekType == SEEKTYPE_Carcass) { if (RaiseAlarm != RAISEALARM_Never) { RaiseAlarm = RAISEALARM_BeforeAttacking; //CyberP: pawns that spot carcasses go for alarm GotoState('Alerting'); } } FindAnotherPlace: //CyberP: New label for another run around SeekLevel--; if (PickDestination()) Goto('GoToLocation'); LookAroundAgain: if (bCanTurnHead) { if (FRand() < 0.3) { if (!bSeekLocation) { PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.0); } } else { if (!bSeekLocation) { PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.0); } } PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); StopBlendAnims(); } else { if (!bSeekLocation) Sleep(0.6); //CyberP: was 1.0 } FindAnotherPlaceAgain: //CyberP: New label for another run around SeekLevel--; if (PickDestination()) Goto('GoToLocation'); DoneSeek: if (bSeekPostCombat) PlayTargetLostSound(); else if (RaiseAlarm != RAISEALARM_BeforeAttacking) //CyberP: bad hack so they don't say anything after alarm trigger PlaySearchGiveUpSound(); bSeekPostCombat = false; SeekPawn = None; if (Orders != 'Seeking') FollowOrders(); else GotoState('Wandering'); ContinueSeek: ContinueFromDoor: FinishAnim(); if (FRand() < 0.7) //CyberP: continue seeking post-opening a door, you dumbasses. { SeekType = SEEKTYPE_Guess; Goto('GoToLocation'); } else Goto('FindAnotherPlace'); } // ---------------------------------------------------------------------- // state Fleeing // // Run like a bat outta hell away from an actor. // ---------------------------------------------------------------------- State Fleeing { function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local Name currentState; local Pawn oldEnemy; local name newLabel; local bool bHateThisInjury; local bool bFearThisInjury; local bool bAttack; if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury)) { currentState = GetStateName(); bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury); bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury); if (bHateThisInjury) IncreaseAgitation(instigatedBy); if (bFearThisInjury) IncreaseFear(instigatedBy, 2.0); oldEnemy = Enemy; bAttack = false; if (SetEnemy(instigatedBy)) { if (!ShouldFlee()) { SwitchToBestWeapon(); if (Weapon != None) bAttack = true; } } else SetEnemy(instigatedBy, , true); if (bAttack) { SetDistressTimer(); SetNextState('HandlingEnemy'); } else { SetDistressTimer(); if (oldEnemy != Enemy) newLabel = 'Begin'; else newLabel = 'ContinueFlee'; SetNextState('Fleeing', newLabel); } GotoDisabledState(damageType, hitPos); } } function SetFall() { StartFalling('Fleeing', 'ContinueFlee'); } function FinishFleeing() { if (bLeaveAfterFleeing) GotoState('Wandering'); else FollowOrders(); } function bool InSeat(out vector newLoc) // hack { local Seat curSeat; local bool bSeat; bSeat = false; foreach RadiusActors(Class'Seat', curSeat, 200) { if (IsOverlapping(curSeat)) { bSeat = true; newLoc = curSeat.Location + vector(curSeat.Rotation+Rot(0, -16384, 0))*(CollisionRadius+curSeat.CollisionRadius+20); break; } } return (bSeat); } function Tick(float deltaSeconds) { UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false); if (IsValidEnemy(Enemy)) { if (EnemyLastSeen > FearSustainTime) FinishFleeing(); } else if (!IsValidEnemy(Enemy, false)) FinishFleeing(); else if (!IsFearful()) FinishFleeing(); Global.Tick(deltaSeconds); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function PickDestination() { local HidePoint hidePoint; local Actor waypoint; local float dist; local float score; local Vector vector1, vector2; local Rotator rotator1; local float tmpDist; local float bestDist; local float bestScore; local FleeCandidates candidates[5]; local int candidateCount; local int maxCandidates; local float maxDist; local int openSlot; local float maxScore; local int i; local bool bReplace; local float angle; local float magnitude; local int iterations; local NearbyProjectileList projList; local bool bSuccess; maxCandidates = 3; // must be <= size of candidates[] arrays maxDist = 10000; // Initialize the list of candidates for (i=0; i<maxCandidates; i++) { candidates[i].score = -1; candidates[i].dist = maxDist+1; } candidateCount = 0; MoveTarget = None; destPoint = None; if (bAvoidHarm) { GetProjectileList(projList, Location); if (IsLocationDangerous(projList, Location)) { vector1 = ComputeAwayVector(projList); rotator1 = Rotator(vector1); if (AIDirectionReachable(Location, rotator1.Yaw, rotator1.Pitch, CollisionRadius+24, VSize(vector1), destLoc)) return; // eck -- hack!!! } } if (Enemy != None) { foreach RadiusActors(Class'HidePoint', hidePoint, maxDist) { // Can the boogeyman see our hiding spot? if (!enemy.LineOfSightTo(hidePoint)) { // More importantly, can we REACH our hiding spot? waypoint = GetNextWaypoint(hidePoint); if (waypoint != None) { // How far is it to the hiding place? dist = VSize(hidePoint.Location - Location); // Determine vectors to the waypoint and our enemy vector1 = enemy.Location - Location; vector2 = waypoint.Location - Location; // Strip out magnitudes from the vectors tmpDist = VSize(vector1); if (tmpDist > 0) vector1 /= tmpDist; tmpDist = VSize(vector2); if (tmpDist > 0) vector2 /= tmpDist; // Add them vector1 += vector2; // Compute a score (a function of angle) score = VSize(vector1); score = 4-(score*score); // Find an empty slot for this candidate openSlot = -1; bestScore = score; bestDist = dist; for (i=0; i<maxCandidates; i++) { // Can we replace the candidate in this slot? if (bestScore > candidates[i].score) bReplace = TRUE; else if ((bestScore == candidates[i].score) && (bestDist < candidates[i].dist)) bReplace = TRUE; else bReplace = FALSE; if (bReplace) { bestScore = candidates[i].score; bestDist = candidates[i].dist; openSlot = i; } } // We found an open slot -- put our candidate here if (openSlot >= 0) { candidates[openSlot].point = hidePoint; candidates[openSlot].waypoint = waypoint; candidates[openSlot].location = waypoint.Location; candidates[openSlot].score = score; candidates[openSlot].dist = dist; if (candidateCount < maxCandidates) candidateCount++; } } } } // Any candidates? if (candidateCount > 0) { // Find a random candidate // (candidates moving AWAY from the enemy have a higher // probability of being chosen than candidates moving // TOWARDS the enemy) maxScore = 0; for (i=0; i<candidateCount; i++) maxScore += candidates[i].score; score = FRand() * maxScore; for (i=0; i<candidateCount; i++) { score -= candidates[i].score; if (score <= 0) break; } destPoint = candidates[i].point; MoveTarget = candidates[i].waypoint; destLoc = candidates[i].location; } else { iterations = 4; magnitude = 400*(FRand()*0.4+0.8); // 400, +/-20% rotator1 = Rotator(Location-Enemy.Location); if (!AIPickRandomDestination(100, magnitude, rotator1.Yaw, 0.6, rotator1.Pitch, 0.6, iterations, FRand()*0.4+0.35, destLoc)) destLoc = Location+(VRand()*1200); // we give up } } else destLoc = Location+(VRand()*1200); // we give up } function BeginState() { StandUp(); Disable('AnimEnd'); //Disable('Bump'); BlockReactions(); if (!bCower) bCanConverse = False; bStasis = False; SetupWeapon(false, true); SetDistress(true); EnemyReadiness = 1.0; //ReactionLevel = 1.0; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); Enable('AnimEnd'); //Enable('Bump'); ResetReactions(); if (!bCower) bCanConverse = True; bStasis = True; } Begin: //EnemyLastSeen = 0; destPoint = None; Surprise: if ((1.0-ReactionLevel)*SurprisePeriod < 0.25) Goto('Flee'); Acceleration=vect(0,0,0); PlaySurpriseSound(); PlayWaiting(); Sleep(FRand()*0.5); if (Enemy != None) TurnToward(Enemy); if (bCower) Goto('Flee'); Sleep(FRand()*0.5+0.5); Flee: if (bLeaveAfterFleeing) { bTransient = true; bDisappear = true; } if (bCower) Goto('Cower'); WaitForLanding(); PickDestination(); Moving: Sleep(0.0); if (enemy == None) { Acceleration = vect(0,0,0); PlayWaiting(); Sleep(2.0); FinishFleeing(); } // Move from pathnode to pathnode until we get where we're going if (destPoint != None) { EnableCheckDestLoc(true); while (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); if (enemy.bDetectable && enemy.AICanSee(destPoint, 1.0, false, false, false, true) > 0) { PickDestination(); EnableCheckDestLoc(false); Goto('Moving'); } if (MoveTarget == destPoint) break; MoveTarget = FindPathToward(destPoint); } EnableCheckDestLoc(false); } else if (PointReachable(destLoc)) { if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); if (enemy.bDetectable && enemy.AICanSee(Self, 1.0, false, false, true, true) > 0) { PickDestination(); Goto('Moving'); } } else { PickDestination(); Goto('Moving'); } Pausing: Acceleration = vect(0,0,0); if (enemy != None) { if (HidePoint(destPoint) != None) { if (ShouldPlayTurn(Location + HidePoint(destPoint).faceDirection)) PlayTurning(); TurnTo(Location + HidePoint(destPoint).faceDirection); } Enable('AnimEnd'); TweenToWaiting(0.2); while (AICanSee(enemy, 1.0, false, false, true, true) <= 0) Sleep(0.25); Disable('AnimEnd'); FinishAnim(); } Goto('Flee'); Cower: if (!InSeat(useLoc)) Goto('CowerContinue'); PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CowerContinue: Acceleration = vect(0,0,0); PlayCowerBegin(); FinishAnim(); PlayCowering(); // behavior 3 - cower and occasionally make short runs while (true) { Sleep(FRand()*3+6); PlayCowerEnd(); FinishAnim(); if (AIPickRandomDestination(60, 150, 0, 0, 0, 0, 2, FRand()*0.3+0.6, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); } PlayCowerBegin(); FinishAnim(); PlayCowering(); } /* behavior 2 - cower forever // don't stop cowering while (true) Sleep(1.0); */ /* behavior 1 - cower only when enemy watching if (enemy != None) { while (AICanSee(enemy, 1.0, false, false, true, true) > 0) Sleep(0.25); } PlayCowerEnd(); FinishAnim(); Goto('Pausing'); */ ContinueFlee: ContinueFromDoor: FinishAnim(); PlayRunning(); if (bCower) Goto('Cower'); else Goto('Moving'); } // ---------------------------------------------------------------------- // state Attacking // // Kill! Kill! Kill! Kill! // ---------------------------------------------------------------------- State Attacking { function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local Pawn oldEnemy; local bool bHateThisInjury; local bool bFearThisInjury; if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury)) { oldEnemy = Enemy; bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury); bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury); if (bHateThisInjury) IncreaseAgitation(instigatedBy, 1.0); if (bFearThisInjury) IncreaseFear(instigatedBy, 2.0); if (ReadyForNewEnemy()) SetEnemy(instigatedBy); if (ShouldFlee()) { SetDistressTimer(); PlayCriticalDamageSound(); if (RaiseAlarm == RAISEALARM_BeforeFleeing) SetNextState('Alerting'); else SetNextState('Fleeing'); } else { SetDistressTimer(); if (oldEnemy != Enemy) PlayNewTargetSound(); SetNextState('Attacking', 'ContinueAttack'); } GotoDisabledState(damageType, hitPos); } } function SetFall() { StartFalling('Attacking', 'ContinueAttack'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Reloading(DeusExWeapon reloadWeapon, float reloadTime) { Global.Reloading(reloadWeapon, reloadTime); if (bReadyToReload) if (IsWeaponReloading()) if (!IsHandToHand()) TweenToShoot(0); } function EDestinationType PickDestination() { local vector distVect; local vector tempVect; local rotator enemyDir; local float magnitude, dista; local float calcMagnitude; local int iterations; local EDestinationType destType; local NearbyProjectileList projList; destPoint = None; destLoc = vect(0, 0, 0); destType = DEST_Failure; if (enemy == None) return (destType); if ((bCrouching && (CrouchTimer > 0))) destType = DEST_SameLocation; else if (bDefensiveStyle) //CyberP: added: enemies don't ALWAYS rush your position when in close proximity { dista = Abs(VSize(Enemy.Location - Location)); if (dista < 768) if (EnemyTimer < EnemyTimeout*0.3 && EnemyTimer > 0 && Weapon.IsA('DeusExWeapon') && DeusExWeapon(Weapon).AccurateRange > 768) destType = DEST_SameLocation; } if (destType == DEST_Failure) { if (AICanShoot(enemy, true, false, 0.025) || ActorReachable(enemy)) { destType = ComputeBestFiringPosition(tempVect); if (destType == DEST_NewLocation) destLoc = tempVect; } } if (destType == DEST_Failure) { MoveTarget = FindPathToward(enemy); if (MoveTarget != None) { if (!bDefendHome || IsNearHome(MoveTarget.Location)) { if (bAvoidHarm) GetProjectileList(projList, MoveTarget.Location); if (!bAvoidHarm || !IsLocationDangerous(projList, MoveTarget.Location)) { destPoint = MoveTarget; destType = DEST_NewLocation; } } } } // Default behavior, so they don't just stand there... if (destType == DEST_Failure) { enemyDir = Rotator(Enemy.Location - Location); //if (IsHandToHand()) //{destType = DEST_NewLocation; destLoc = Location+(VRand()*1200);} //CyberP: run away if cannot reach player with melee if (AIPickRandomDestination(60, 150, enemyDir.Yaw, 0.5, enemyDir.Pitch, 0.5, 2, FRand()*0.4+0.35, tempVect)) { if (!bDefendHome || IsNearHome(tempVect)) { destType = DEST_NewLocation; destLoc = tempVect; } } } return (destType); } function bool FireIfClearShot() { local DeusExWeapon dxWeapon; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) { if (dxWeapon.bHandToHand) bDefendHome = False; //CyberP: campers no longer camp if out of ammo if ((dxWeapon.AIFireDelay > 0) && (FireTimer > 0)) return false; else if (dxWeapon.IsA('WeaponRifle') && FRand() < 0.8 && Enemy.IsA('DeusExPlayer') && DeusExPlayer(Enemy).CombatDifficulty < 3) return false; //CyberP: rand as to whether snipers can fire, to simulate taking time to aim. else if (AICanShoot(enemy, true, true, 0.025)) { Weapon.Fire(0); FireTimer = dxWeapon.AIFireDelay; return true; } else return false; } else return false; } function CheckAttack(bool bPlaySound) { local bool bCriticalDamage; local bool bOutOfAmmo; local Pawn oldEnemy; local bool bAllianceSwitch; oldEnemy = enemy; bAllianceSwitch = false; if (!IsValidEnemy(enemy)) { if (IsValidEnemy(enemy, false)) bAllianceSwitch = true; SetEnemy(None, 0, true); } if (enemy == None) { if (Orders == 'Attacking') { FindOrderActor(); SetEnemy(Pawn(OrderActor), 0, true); } } if (ReadyForNewEnemy()) FindBestEnemy(false); if (enemy == None) { Enemy = oldEnemy; // hack if (bPlaySound) { if (bAllianceSwitch) PlayAllianceFriendlySound(); else PlayAreaSecureSound(); } Enemy = None; if (Orders != 'Attacking') FollowOrders(); else GotoState('Wandering'); return; } SwitchToBestWeapon(); if (bCrouching && (CrouchTimer <= 0) && !ShouldCrouch()) { EndCrouch(); TweenToShoot(0.15); } bCriticalDamage = False; bOutOfAmmo = False; if (ShouldFlee()) bCriticalDamage = True; else if (Weapon == None) bOutOfAmmo = True; else if (Weapon.ReloadCount > 0) { if (Weapon.AmmoType == None) bOutOfAmmo = True; else if (Weapon.AmmoType.AmmoAmount < 1) bOutOfAmmo = True; } if (bCriticalDamage || bOutOfAmmo) { if (bPlaySound) { if (bCriticalDamage) PlayCriticalDamageSound(); else if (bOutOfAmmo) PlayOutOfAmmoSound(); } if (RaiseAlarm == RAISEALARM_BeforeFleeing) GotoState('Alerting'); else GotoState('Fleeing'); } else if (bPlaySound && (oldEnemy != Enemy)) PlayNewTargetSound(); } function Tick(float deltaSeconds) { local bool bCanSee; local float yaw; local vector lastLocation; local Pawn lastEnemy; local float surpriseTime; Global.Tick(deltaSeconds); if (CrouchTimer > 0) { CrouchTimer -= deltaSeconds; if (CrouchTimer < 0) CrouchTimer = 0; } EnemyTimer += deltaSeconds; UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false); if (IsA('Greasel')) { ShouldGreaselMelee(); } if ((Enemy != None) && HasEnemyTimedOut()) { lastLocation = Enemy.Location; lastEnemy = Enemy; FindBestEnemy(true); if (Enemy == None) { SetSeekLocation(lastEnemy, lastLocation, SEEKTYPE_Guess, true); GotoState('Seeking'); } } else if (bCanFire && (Enemy != None)) { ViewRotation = Rotator(Enemy.Location-Location); if (bFacingTarget) FireIfClearShot(); else if (!bMustFaceTarget) { yaw = (ViewRotation.Yaw-Rotation.Yaw) & 0xFFFF; if (yaw >= 32768) yaw -= 65536; yaw = Abs(yaw)*360/32768; // 0-180 x 2 if (yaw <= FireAngle) FireIfClearShot(); } } //UpdateReactionLevel(true, deltaSeconds); } function bool IsHandToHand() { if (Weapon != None) { if (DeusExWeapon(Weapon) != None) { if (DeusExWeapon(Weapon).bHandToHand && !DeusExWeapon(Weapon).IsA('WeaponHideAGun')) return true; else return false; } else return false; } else return false; } function bool ReadyForWeapon() { local bool bReady; bReady = false; if (DeusExWeapon(weapon) != None) { if (DeusExWeapon(weapon).bReadyToFire) if (!IsWeaponReloading()) bReady = true; } if (!bReady) if (enemy == None) bReady = true; if (!bReady) if (!AICanShoot(enemy, true, false, 0.025)) bReady = true; return (bReady); } function bool ShouldCrouch() { if (bCanCrouch && !Region.Zone.bWaterZone && !IsHandToHand() && ((enemy != None) && (VSize(enemy.Location-Location) > 100)) && //CyberP: 300 ((DeusExWeapon(Weapon) == None) || DeusExWeapon(Weapon).bUseWhileCrouched)) return true; else return false; } function StartCrouch() { if (!bCrouching) { bCrouching = true; SetBasedPawnSize(CollisionRadius, GetCrouchHeight()); CrouchTimer = 1.0+FRand()*0.75; //CyberP: was *0.5 } } function EndCrouch() { if (bCrouching) { bCrouching = false; ResetBasedPawnSize(); } } function SystemShocked() { local Vector vec; local ShockRingProjectile shock; if (AnimSequence == 'Pickup') { vec = Location; vec.Z += 10; shock = Spawn(class'ShockRingProjectile',,, vec, rot(16384,0,0)); if (shock!=none) {shock.ScaleGlow=0.0001; shock.bUnlit=False;} shock = Spawn(class'ShockRingProjectile',,, vec, rot(16384,0,0)); if (shock!=none) {shock.ScaleGlow=0.0001; shock.bUnlit=False;} Spawn(class'ShockRingProjectile',,, vec, rot(16384,0,0)); Spawn(class'SphereEffectShield',,, vec); PlaySound(sound'shootrail',SLOT_None,2,,2048); } bRadialBlastClamp=True; //SetPhysics(PHYS_Walking); } function ShouldMelee() { local DeusExPlayer dxPlayer; local vector HitNormal, HitLocation, StartTrace, EndTrace; local float dista; dxPlayer = DeusExPlayer(GetPlayerPawn()); dista = Abs(VSize(dxPlayer.Location - Location)); //CyberP: I should probably change this to apply to enemy rather than just dxPlayer if (dista < 128) { StartTrace = Location; EndTrace = Location + (Vector(Rotation)) * (CollisionRadius+40); if (dxPlayer != None && Trace(HitLocation, HitNormal, EndTrace, StartTrace, True) == dxPlayer) { if (Physics == PHYS_Walking && !Region.Zone.bWaterZone) { bCommandoMelee = True; if (IsA('MJ12Commando')) PlaySound(Sound'TurretLocked',SLOT_None,0.5,,512,1.2); } } } } function ShouldGreaselMelee() //CyberP: TODO: trace a few feet up for geometry so greasel doesn't jump in vents etc? { local vector HitNormal, HitLocation, StartTrace, EndTrace; local float dista; dista = Abs(VSize(enemy.Location - Location)); if (!Region.Zone.bWaterZone && Physics != PHYS_Falling && AnimSequence != 'Attack') { if (dista < 288 && dista > 96) { StartTrace = Location; EndTrace = Location + (Vector(Rotation)) * 288; if (Trace(HitLocation, HitNormal, EndTrace, StartTrace, True) == enemy) { bGreaselShould = True; } else { bGreaselShould = False; } } } } function ShouldRadialBlast() { local float dist; if (GroundSpeed > default.GroundSpeed && FRand() < 0.015) { if (bRadialBlastClamp) { if (Enemy!=none) { dist = Abs(VSize(Enemy.Location - Location)); //CyberP: should change this to trace so they cannot do so through walls. if (dist < 320 && (Abs(Location.Z-Enemy.Location.Z) < 96)) { bRadialBlastClamp=False; Velocity = vect(0,0,1); PlayAnimPivot('Pickup',0.6,0.1); } } } } } function checkNadeThrow() { local DeusExPlayer playa; local HumanMilitary P; local float dista; local Vector HitNormal, HitLocation, StartTrace, EndTrace, offset; local int i; if (Enemy != None && Enemy.IsA('DeusExPlayer')) playa = DeusExPlayer(Enemy); if (playa != none && bCanNade == False) { //CyberP: trace towards player. If we manage to trace end near the player AND there is no other pawns close to that location, then we can throw. dista = Abs(VSize(playa.Location - Location)); if (dista > 768 && dista < 1536 && (Abs(Location.Z-playa.Location.Z) < 160)) { offset = Location; offset.Z += 8; StartTrace = offset; EndTrace = playa.Location; ForEach Trace(HitLocation, HitNormal, EndTrace, StartTrace, True).RadiusActors(Class'DeusExPlayer',playa,320,HitLocation) { i++; if (i == 1) { i = 0; ForEach playa.RadiusActors(Class'HumanMilitary',P,640,playa.Location) { i++; } if (i < 1) bCanNade=True; } } } } } function nadeThrow() //CyberP: the throwing itself is exceptionally bad, but hey, it is still better than the vanilla nade throwing. { local GasGrenade gasNade; local EMPGrenade empNade; local vector flareOffset; local DeusExPlayer playa; local float dista; flareOffset = Location; flareOffset.Z += 50; playa = DeusExPlayer(GetPlayerPawn()); dista = Abs(VSize(playa.Location - Location)); if (IsA('Terrorist') || IsA('UNATCOTroop') || IsA('RiotCop')) { gasNade = Spawn(class'GasGrenade',self,,flareOffset); if (gasNade != None) { if (playa != None && playa.CombatDifficulty > 2) gasNade.blastRadius += 256; if (dista < 1000) { gasNade.Velocity = Vector(Rotation)*1000 + (VRand()*6); gasNade.Velocity.Z += 300; } else if (dista < 1300) { gasNade.Velocity = Vector(Rotation)*1150 + (VRand()*6); gasNade.Velocity.Z += 350; } else { gasNade.Velocity = Vector(Rotation)*1300 + (VRand()*6); gasNade.Velocity.Z += 400; } } } else if (IsA('MJ12Troop') || IsA('Soldier') || IsA('MJ12Elite')) { if (FRand() < 0.5) { gasNade = Spawn(class'GasGrenade',self,,flareOffset); if (gasNade != None) { if (playa != None && playa.CombatDifficulty > 2) gasNade.blastRadius += 256; if (dista < 900) { gasNade.Velocity = Vector(Rotation)*1000 + (VRand()*6); gasNade.Velocity.Z += 300; } else if (dista < 1300) { gasNade.Velocity = Vector(Rotation)*1150 + (VRand()*6); gasNade.Velocity.Z += 350; } else { gasNade.Velocity = Vector(Rotation)*1300 + (VRand()*6); gasNade.Velocity.Z += 400; } } } else { empNade = Spawn(class'EMPGrenade',self,,flareOffset); if (empNade != None) { if (playa != None && playa.CombatDifficulty > 2) empNade.blastRadius += 256; if (dista < 900) { empNade.Velocity = Vector(Rotation)*1000 + (VRand()*6); empNade.Velocity.Z += 300; } else if (dista < 1300) { empNade.Velocity = Vector(Rotation)*1150 + (VRand()*6); empNade.Velocity.Z += 350; } else { empNade.Velocity = Vector(Rotation)*1300 + (VRand()*6); empNade.Velocity.Z += 400; } } } } } function BeginState() { StandUp(); // hack if (MaxRange < MinRange+10) MaxRange = MinRange+10; bCanFire = false; bFacingTarget = false; SwitchToBestWeapon(); //EnemyLastSeen = 0; BlockReactions(); bCanConverse = False; bAttacking = True; bStasis = False; SetDistress(true); CrouchTimer = 0; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bCanFire = false; bFacingTarget = false; ResetReactions(); bCanConverse = True; bAttacking = False; bStasis = True; bReadyToReload = false; EndCrouch(); } Begin: if (Enemy == None) GotoState('Seeking'); //EnemyLastSeen = 0; CheckAttack(false); Surprise: if ((1.0-ReactionLevel)*SurprisePeriod < 0.25) Goto('BeginAttack'); Acceleration=vect(0,0,0); PlaySurpriseSound(); PlayWaiting(); while (ReactionLevel < 1.0) { TurnToward(Enemy); Sleep(0); } BeginAttack: EnemyReadiness = 1.0; ReactionLevel = 1.0; if (PlayerAgitationTimer > 0) PlayAllianceHostileSound(); else PlayTargetAcquiredSound(); if (PlayBeginAttack()) { Acceleration = vect(0,0,0); TurnToward(enemy); FinishAnim(); } RunToRange: bCanFire = false; bFacingTarget = false; bReadyToReload = false; EndCrouch(); if (Physics == PHYS_Falling) TweenToRunning(0.05); WaitForLanding(); if (!IsWeaponReloading() || bCrouching) { if (ShouldPlayTurn(Enemy.Location)) PlayTurning(); TurnToward(enemy); } else Sleep(0); bCanFire = true; while (PickDestination() == DEST_NewLocation) { if (bCanStrafe && ShouldStrafe()) { PlayRunningAndFiring(); if (destPoint != None) StrafeFacing(destPoint.Location, enemy); else StrafeFacing(destLoc, enemy); bFacingTarget = true; if (bGrenadier) //CyberP: nade throw { if (FRand() < 0.025) checkNadeThrow(); if (bCanNade) { PlayAnimPivot('Attack',1.1,0.1); Sleep(0.3); nadeThrow(); bCanNade = False; FinishAnim(); PlayWaiting(); Sleep(0.1); } } } else { bFacingTarget = false; PlayRunning(); if (IsA('Greasel')) { if (bGreaselShould) { FinishAnim(); bBiteClamp = False; bCanFire = False; RotationRate.Yaw = 2; switchToBestWeapon(); PlayAnimPivot('Attack',2.6,0.1); SetPhysics(PHYS_Falling); Velocity = (Vector(Rotation)) * 400; Velocity.Z = 260 + (FRand()*20); bGreaselShould=False; Sleep(0.3); FinishAnim(); LoopAnimPivot('Run',runAnimMult,0.1); } } if (destPoint != None) MoveToward(destPoint, MaxDesiredSpeed); else MoveTo(destLoc, MaxDesiredSpeed); } CheckAttack(true); } Fire: bCanFire = false; bFacingTarget = false; Acceleration = vect(0, 0, 0); SwitchToBestWeapon(); if (FRand() > 0.5) bUseSecondaryAttack = true; else bUseSecondaryAttack = false; if (IsHandToHand()) TweenToAttack(0.15); //CyberP: 0.15 else if (ShouldCrouch() && (FRand() < CrouchRate)) { TweenToCrouchShoot(0.15); FinishAnim(); StartCrouch(); } else TweenToShoot(0.15); if (!IsWeaponReloading() || bCrouching) TurnToward(enemy); FinishAnim(); if (IsA('HumanMilitary')) { ShouldMelee(); if (bCommandoMelee) { bCommandoMelee = False; FinishAnim(); if (IsA('MJ12Commando')) PlayAnimPivot('Attack',1.8,0.1); else PlayAnimPivot('Attack',1.4,0.1); sleep(0.3); if (!IsInState('Dying') && !IsInState('TakingHit')) { Enemy.TakeDamage(8,self,Enemy.Location,vect(0,0,0),'Shot'); if (Enemy.IsA('DeusExPlayer')) DeusExPlayer(Enemy).ShakeView(0.2,512,12); Enemy.SetPhysics(PHYS_Falling); if (IsA('MJ12Commando')) Enemy.Velocity -= Vector(Enemy.ViewRotation) * 900; else Enemy.Velocity -= Vector(Enemy.ViewRotation) * 350; Enemy.Velocity.Z = 20; Enemy.PlaySound(sound'pl_fallpain3',SLOT_None,2.0,,,1.2); sleep(0.05); FinishAnim(); } } } if (IsA('Greasel')) { if (bGreaselShould) { FinishAnim(); bBiteClamp = False; bCanFire = False; RotationRate.Yaw = 2; switchToBestWeapon(); PlayAnimPivot('Attack',2.6,0.1); SetPhysics(PHYS_Falling); Velocity = (Vector(Rotation)) * 400; Velocity.Z = 260 + (FRand()*20); bGreaselShould=False; Sleep(0.3); FinishAnim(); LoopAnimPivot('Run',runAnimMult,0.1); } } else if (bGrenadier) //CyberP: nade throw { if ((IsA('MJ12Troop') || IsA('MJ12Elite')) && FRand() < 0.03) checkNadeThrow(); else if (FRand() < 0.02) checkNadeThrow(); if (bCanNade) { PlayAnimPivot('Attack',1.1,0.1); Sleep(0.3); nadeThrow(); bCanNade = False; FinishAnim(); PlayWaiting(); Sleep(0.1); } } else if (IsA('MIB')) { ShouldRadialBlast(); if (AnimSequence == 'Pickup') { sleep(0.7); SystemShocked(); } } bReadyToReload = true; ContinueFire: while (!ReadyForWeapon()) { if (IsA('Greasel')) { if (bGreaselShould) { FinishAnim(); bBiteClamp = False; bCanFire = False; RotationRate.Yaw = 2; switchToBestWeapon(); PlayAnimPivot('Attack',2.6,0.1); SetPhysics(PHYS_Falling); Velocity = (Vector(Rotation)) * 400; Velocity.Z = 260 + (FRand()*20); bGreaselShould=False; Sleep(0.3); FinishAnim(); LoopAnimPivot('Run',runAnimMult,0.1); } } if (PickDestination() != DEST_SameLocation) Goto('RunToRange'); CheckAttack(true); if (!IsWeaponReloading() || bCrouching) TurnToward(enemy); else Sleep(0); } CheckAttack(true); if (!FireIfClearShot()) Goto('ContinueAttack'); bReadyToReload = false; if (bCrouching) PlayCrouchShoot(); else if (IsHandToHand()) PlayAttack(); else PlayShoot(); FinishAnim(); if (IsA('MIB')) { ShouldRadialBlast(); if (AnimSequence == 'Pickup') { sleep(0.7); SystemShocked(); } } if (IsA('HumanMilitary')) { ShouldMelee(); if (bCommandoMelee) { bCommandoMelee = False; FinishAnim(); if (IsA('MJ12Commando')) PlayAnimPivot('Attack',1.8,0.1); else PlayAnimPivot('Attack',1.5,0.1); sleep(0.35); if (!IsInState('Dying') && !IsInState('TakingHit')) { Enemy.TakeDamage(8,self,Enemy.Location,vect(0,0,0),'Shot'); if (Enemy.IsA('DeusExPlayer')) DeusExPlayer(Enemy).ShakeView(0.2,512,12); Enemy.SetPhysics(PHYS_Falling); if (IsA('MJ12Commando')) Enemy.Velocity -= Vector(Enemy.ViewRotation) * 900; else Enemy.Velocity -= Vector(Enemy.ViewRotation) * 400; Enemy.Velocity.Z = 20; Enemy.PlaySound(sound'pl_fallpain3',SLOT_None,2.0,,,1.2); sleep(0.05); FinishAnim(); } } } if (bGrenadier) //CyberP: nade throw { if ((IsA('MJ12Troop') || IsA('MJ12Elite')) && FRand() < 0.03) checkNadeThrow(); else if (FRand() < 0.02) checkNadeThrow(); if (bCanNade) { PlayAnimPivot('Attack',1.1,0.1); Sleep(0.3); nadeThrow(); bCanNade = False; FinishAnim(); PlayWaiting(); Sleep(0.1); } } if (FRand() > 0.5) bUseSecondaryAttack = true; else bUseSecondaryAttack = false; bReadyToReload = true; if (!IsHandToHand()) { if (bCrouching) TweenToCrouchShoot(0); else TweenToShoot(0); } CheckAttack(true); if (PickDestination() != DEST_NewLocation) { if (!IsWeaponReloading() || bCrouching) TurnToward(enemy); else Sleep(0); Goto('ContinueFire'); } Goto('RunToRange'); ContinueAttack: ContinueFromDoor: CheckAttack(true); if (PickDestination() != DEST_NewLocation) Goto('Fire'); else Goto('RunToRange'); } // ---------------------------------------------------------------------- // state Alerting // // Warn other NPCs that an enemy is about // ---------------------------------------------------------------------- State Alerting { function SetFall() { StartFalling('Alerting', 'ContinueAlert'); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { if (bAcceptBump) { if (bumper == AlarmActor) { bAcceptBump = False; GotoState('Alerting', 'SoundAlarm'); } } // Handle conversations, if need be Global.Bump(bumper); } function bool IsAlarmReady(Actor actorAlarm) { local bool bReady; local AlarmUnit alarm; bReady = false; alarm = AlarmUnit(actorAlarm); if ((alarm != None) && !alarm.bDeleteMe) if (!alarm.bActive) if ((alarm.associatedPawn == None) || (alarm.associatedPawn == self)) bReady = true; return bReady; } function TriggerAlarm() { local DeusExPlayer playa; if ((AlarmActor != None) && !AlarmActor.bDeleteMe) { playa = DeusExPlayer(GetPlayerPawn()); if (AlarmActor.hackStrength > 0) // make sure the alarm hasn't been hacked AlarmActor.Trigger(self, Enemy); else if (playa != None && playa.PerkNamesArray[10] == 1) //CyberP: shock the pawn TakeDamage(200,self,vect(0,0,0),vect(0,0,0),'Stunned'); } } function bool IsAlarmInRange(AlarmUnit alarm) { local bool bInRange; bInRange = false; if ((alarm != None) && !alarm.bDeleteMe) if ((VSize((alarm.Location-Location)*vect(1,1,0)) < (CollisionRadius+alarm.CollisionRadius+24)) && (Abs(alarm.Location.Z-Location.Z) < (CollisionHeight+alarm.CollisionHeight))) bInRange = true; return (bInRange); } function vector FindAlarmPosition(Actor alarm) { local vector alarmPos; alarmPos = alarm.Location; alarmPos += vector(alarm.Rotation.Yaw*rot(0,1,0))*(CollisionRadius+alarm.CollisionRadius); return (alarmPos); } function bool GetNextAlarmPoint(AlarmUnit alarm) { local vector alarmPoint; local bool bValid; destPoint = None; destLoc = vect(0,0,0); bValid = false; if ((alarm != None) && !alarm.bDeleteMe) { alarmPoint = FindAlarmPosition(alarm); if (PointReachable(alarmPoint)) { destLoc = alarmPoint; bValid = true; } else { MoveTarget = FindPathTo(alarmPoint); if (MoveTarget != None) { destPoint = MoveTarget; bValid = true; } } } return (bValid); } function AlarmUnit FindTarget() { local ScriptedPawn pawnAlly; local AlarmUnit alarm; local float dist; local AlarmUnit bestAlarm; local float bestDist; bestAlarm = None; // Do we have any allies on this level? foreach AllActors(Class'ScriptedPawn', pawnAlly) if (GetPawnAllianceType(pawnAlly) == ALLIANCE_Friendly) break; // Yes, so look for an alarm box that isn't active... if (pawnAlly != None) { foreach RadiusActors(Class'AlarmUnit', alarm, 3200) //CyberP: was 2400 { if (GetAllianceType(alarm.Alliance) != ALLIANCE_Hostile) { dist = VSize((Location-alarm.Location)*vect(1,1,2)); // use squished sphere if ((bestAlarm == None) || (dist < bestDist)) { bestAlarm = alarm; bestDist = dist; } } } // Is the nearest alarm already going off? And can we reach it? if (!IsAlarmReady(bestAlarm) || !GetNextAlarmPoint(bestAlarm)) bestAlarm = None; } // Return our target alarm box return (bestAlarm); } function bool PickDestination() { local bool bDest; local AlarmUnit alarm; // Init destPoint = None; destLoc = vect(0, 0, 0); bDest = false; // Find an alarm we can trigger alarm = FindTarget(); if (alarm != None) { // Find a way to get there AlarmActor = alarm; alarm.associatedPawn = self; bDest = true; // if alarm != none, we've already computed the route to the alarm } // Return TRUE if we were successful return (bDest); } function BeginState() { StandUp(); //Disable('AnimEnd'); bAcceptBump = False; bCanConverse = False; AlarmActor = None; bStasis = False; BlockReactions(); SetupWeapon(false); SetDistress(false); EnemyReadiness = 1.0; ReactionLevel = 1.0; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bAcceptBump = False; //Enable('AnimEnd'); bCanConverse = True; if (AlarmActor != None) if (AlarmActor.associatedPawn == self) AlarmActor.associatedPawn = None; AlarmActor = None; bStasis = True; } Begin: if (Enemy == None && RaiseAlarm != RAISEALARM_BeforeAttacking) //CyberP: added extra condition for pawns who spot carci GotoState('Seeking'); //EnemyLastSeen = 0; destPoint = None; if (RaiseAlarm == RAISEALARM_Never) GotoState('Fleeing'); if (AlarmTimer > 0) PlayGoingForAlarmSound(); Alert: if (AlarmTimer > 0) Goto('Done'); WaitForLanding(); if (!PickDestination()) Goto('Done'); Moving: // Can we go somewhere? bAcceptBump = True; EnableCheckDestLoc(true); while (true) { if (destPoint != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } else { if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); CheckDestLoc(destLoc); } if (IsAlarmInRange(AlarmActor)) break; else if (!GetNextAlarmPoint(AlarmActor)) break; } EnableCheckDestLoc(false); SoundAlarm: Acceleration=vect(0,0,0); bAcceptBump = False; if (IsAlarmInRange(AlarmActor)) { TurnToward(AlarmActor); PlayPushing(); FinishAnim(); TriggerAlarm(); } Done: bAcceptBump = False; if (RaiseAlarm == RAISEALARM_BeforeAttacking) GotoState('Attacking'); else GotoState('Fleeing'); ContinueAlert: ContinueFromDoor: Goto('Alert'); } // ---------------------------------------------------------------------- // state Shadowing // // Quietly tail another character // ---------------------------------------------------------------------- State Shadowing { function SetFall() { StartFalling('Shadowing', 'ContinueShadow'); } function Tick(float deltaSeconds) { local bool bMove; local float deltaValue; Global.Tick(deltaSeconds); deltaValue = deltaSeconds; // If we're running, and we can see our target, STOP RUNNING! if (bRunningStealthy) { UpdateActorVisibility(orderActor, deltaValue, 0.0, false); deltaValue = 0; if (EnemyLastSeen <= 0) { bRunningStealthy = False; PlayWalking(); DesiredSpeed = GetWalkingSpeed(); } } // Are we stopped? if (bPausing) { // Can we see our target? bMove = False; UpdateActorVisibility(orderActor, deltaValue, 0.5, false); deltaValue = 0; // No -- move toward him! if (EnemyLastSeen > 0.5) bMove = True; // We can see him, and we're staring... else if (bStaring) { // ...can he see us staring at him? if ((Pawn(orderActor) != None) && (Pawn(orderActor).AICanSee(self, , false, true, false, false) > 0)) bMove = True; // Time to look inconspicuous } // Move if we need to if (bMove) { if (bStaring) GotoState('Shadowing', 'StopStaring'); else GotoState('Shadowing', 'StopPausing'); bPausing = False; bStaring = False; } } } function Bump(actor bumper) { if (bAcceptBump) { // If we get bumped by another actor while we wait, start wandering again bAcceptBump = False; bPausing = False; bStaring = False; Disable('AnimEnd'); GotoState('Shadowing', 'Shadow'); } // Handle conversations, if need be Global.Bump(bumper); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function float DistanceToTarget() { return (VSize(Location-orderActor.Location)); } function bool PickDestination() { local Actor destActor; local Vector distVect; local Rotator relativeRotation; local float magnitude; local float minDist; local float maxDist; local float bestDist; local bool bDest; local float dist; // Init destPoint = None; destLoc = vect(0, 0, 0); // Conveniences destActor = orderActor; minDist = 400; maxDist = 700; bestDist = (maxDist+minDist)*0.5; distVect = Location - destActor.Location; magnitude = VSize(distVect); bDest = False; // Can we see the target? if (AICanSee(destActor, , false, false, false, true) > 0) { relativeRotation = Rotator(distVect); // How far will we go? dist = (wanderlust*300+150) * (FRand()*0.2+0.9); // 150-450, +/-10% // Move around inconspicuously, like we're just wandering if (magnitude < minDist) // too close -- move away bDest = AIPickRandomDestination(100, dist, relativeRotation.Yaw, 0.8, relativeRotation.Pitch, 0.8, 3, FRand()*0.4+0.35, destLoc); else if (magnitude < maxDist) // just right -- move normally bDest = AIPickRandomDestination(100, dist, relativeRotation.Yaw+32768, 0, -relativeRotation.Pitch, 0, 2, FRand()*0.4+0.35, destLoc); else // too far -- move closer bDest = AIPickRandomDestination(100, dist, relativeRotation.Yaw+32768, 0.8, -relativeRotation.Pitch, 0.8, 3, FRand()*0.4+0.35, destLoc); } // Nope -- find a path towards him else { MoveTarget = FindPathToward(destActor); if (MoveTarget != None) { if (!MoveTarget.Region.Zone.bWaterZone && (MoveTarget.Physics != PHYS_Falling)) { destPoint = MoveTarget; bDest = True; } } } // Return TRUE if we found a place to go return (bDest); } function BeginState() { StandUp(); Disable('AnimEnd'); bRunningStealthy = False; bPausing = False; bStaring = False; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = False; Enable('AnimEnd'); bRunningStealthy = False; bPausing = False; bStaring = False; bStasis = True; } Begin: EnemyLastSeen = 0; destPoint = None; Shadow: WaitForLanding(); Moving: Sleep(0.0); // Can we go somewhere? if (PickDestination()) { // Are we going to a navigation point? if (destPoint != None) { if (MoveTarget != None) { // Run if we're too far away, and we can't see our target if ((DistanceToTarget() > 900) && (AICanSee(orderActor, , false, true, true, true) <= 0)) { bRunningStealthy = True; if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); } // Otherwise, walk nonchalantly else { bRunningStealthy = False; if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); } } } // No pathnode, so walk to a point else { bRunningStealthy = False; if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); } } // Can we see the target? If not, keep walking if (AICanSee(orderActor, , false, false, false, true) <= 0) Goto('Moving'); Pausing: // Stop bRunningStealthy = False; Acceleration = vect(0, 0, 0); // Can the target see us? If not, stare! if (orderActor.IsA('Pawn') && Pawn(orderActor).AICanSee(self, , false, true, false, false) <= 0) Goto('Staring'); // Stop normally sleepTime = 6.0; Enable('AnimEnd'); TweenToWaiting(0.2); bAcceptBump = True; sleepTime *= (-0.9*restlessness) + 1; bStaring = False; bPausing = True; Sleep(sleepTime); StopPausing: // Time to move again bPausing = False; bStaring = False; Disable('AnimEnd'); bAcceptBump = False; FinishAnim(); Goto('Shadow'); Staring: // Stare at the target PlayTurning(); TurnToward(orderActor); Enable('AnimEnd'); TweenToWaiting(0.2); // Don't move 'til he looks at us bAcceptBump = True; bStaring = True; bPausing = True; while (true) { PlayTurning(); TurnToward(orderActor); TweenToWaiting(0.2); Sleep(0.25); } StopStaring: // He's looking, or we can't see him -- time to move bPausing = False; bStaring = False; Disable('AnimEnd'); bAcceptBump = False; FinishAnim(); Goto('Shadow'); ContinueShadow: ContinueFromDoor: FinishAnim(); PlayRunning(); Goto('Moving'); } // ---------------------------------------------------------------------- // state Following // // Follow an actor // ---------------------------------------------------------------------- state Following { function SetFall() { StartFalling('Following', 'ContinueFollow'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); if (BackpedalTimer >= 0) BackpedalTimer += deltaSeconds; animTimer[1] += deltaSeconds; if ((Physics == PHYS_Walking) && (orderActor != None)) { if (Acceleration == vect(0,0,0)) LookAtActor(orderActor, true, true, true, 0, 0.25); else PlayTurnHead(LOOK_Forward, 1.0, 0.25); } } function bool PickDestination() { local float dist; local float extra; local float distMax; local int dir; local rotator rot; local bool bSuccess; bSuccess = false; destPoint = None; destLoc = vect(0, 0, 0); extra = orderActor.CollisionRadius + CollisionRadius; dist = VSize(orderActor.Location - Location); dist -= extra; if (dist < 0) dist = 0; if ((dist > 180) || (AICanSee(orderActor, , false, false, false, true) <= 0)) { if (ActorReachable(orderActor)) { rot = Rotator(orderActor.Location - Location); distMax = (dist-180)+45; if (distMax > 80) distMax = 80; bSuccess = AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 0, distMax, destLoc); } else { MoveTarget = FindPathToward(orderActor); if (MoveTarget != None) { destPoint = MoveTarget; bSuccess = true; } } BackpedalTimer = -1; } else if (dist < 60) { if (BackpedalTimer < 0) BackpedalTimer = 0; if (BackpedalTimer > 1.0) // give the player enough time to converse, if he wants to { rot = Rotator(Location - orderActor.Location); bSuccess = AIDirectionReachable(orderActor.Location, rot.Yaw, rot.Pitch, 60+extra, 120+extra, destLoc); } } else BackpedalTimer = -1; return (bSuccess); } function BeginState() { StandUp(); //Disable('AnimEnd'); bStasis = False; SetupWeapon(false); SetDistress(false); BackpedalTimer = -1; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = False; //Enable('AnimEnd'); bStasis = True; StopBlendAnims(); } Begin: Acceleration = vect(0, 0, 0); destPoint = None; if (orderActor == None) GotoState('Standing'); if (!PickDestination()) Goto('Wait'); Follow: if (destPoint != None) { if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } else Sleep(0.0); // this shouldn't happen } else { if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); CheckDestLoc(destLoc); } if (PickDestination()) Goto('Follow'); Wait: //PlayTurning(); //TurnToward(orderActor); PlayWaiting(); WaitLoop: Acceleration=vect(0,0,0); Sleep(0.0); if (!PickDestination()) Goto('WaitLoop'); else Goto('Follow'); ContinueFollow: ContinueFromDoor: Acceleration=vect(0,0,0); if (PickDestination()) Goto('Follow'); else Goto('Wait'); } // ---------------------------------------------------------------------- // state WaitingFor // // Wait for a pawn to become visible, then move to it // ---------------------------------------------------------------------- state WaitingFor { function SetFall() { StartFalling('WaitingFor', 'ContinueFollow'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { // If we hit the guy we're going to, end the state if (bumper == OrderActor) GotoState('WaitingFor', 'Done'); // Handle conversations, if need be Global.Bump(bumper); } function Touch(actor toucher) { // If we hit the guy we're going to, end the state if (toucher == OrderActor) GotoState('WaitingFor', 'Done'); // Handle conversations, if need be Global.Touch(toucher); } function BeginState() { StandUp(); //BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = True; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); //ResetReactions(); bStasis = True; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) GotoState('Idle'); PlayWaiting(); Wait: Sleep(1.0); if (AICanSee(orderActor, 1.0, false, true, false, true) <= 0) Goto('Wait'); bStasis = False; Follow: if (IsOverlapping(orderActor)) Goto('Done'); MoveTarget = GetNextWaypoint(orderActor); if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) && (MoveTarget.Physics != PHYS_Falling)) { if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn')) { if (GetNextVector(orderActor, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CheckDestLoc(useLoc); } else Goto('Pause'); } else { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } if (IsOverlapping(orderActor)) Goto('Done'); else Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: GotoState('Standing'); ContinueFollow: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state GoingTo // // Move to an actor. // ---------------------------------------------------------------------- state GoingTo { function SetFall() { StartFalling('GoingTo', 'ContinueGo'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { // If we hit the guy we're going to, end the state if (bumper == OrderActor) GotoState('GoingTo', 'Done'); // Handle conversations, if need be Global.Bump(bumper); } function Touch(actor toucher) { // If we hit the guy we're going to, end the state if (toucher == OrderActor) GotoState('GoingTo', 'Done'); // Handle conversations, if need be Global.Touch(toucher); } function BeginState() { StandUp(); //BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = False; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); //ResetReactions(); bStasis = True; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: if (IsOverlapping(orderActor)) Goto('Done'); MoveTarget = GetNextWaypoint(orderActor); if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) && (MoveTarget.Physics != PHYS_Falling)) { if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn')) { if (GetNextVector(orderActor, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayWalking(); MoveTo(useLoc, GetWalkingSpeed()); CheckDestLoc(useLoc); } else Goto('Pause'); } else { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } if (IsOverlapping(orderActor)) Goto('Done'); else Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (orderActor.IsA('PatrolPoint')) TurnTo(Location + PatrolPoint(orderActor).lookdir); GotoState('Standing'); ContinueGo: ContinueFromDoor: PlayWalking(); Goto('Follow'); } // ---------------------------------------------------------------------- // state RunningTo // // Move to an actor really fast. // ---------------------------------------------------------------------- state RunningTo { function SetFall() { StartFalling('RunningTo', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { // If we hit the guy we're going to, end the state if (bumper == OrderActor) GotoState('RunningTo', 'Done'); // Handle conversations, if need be Global.Bump(bumper); } function Touch(actor toucher) { // If we hit the guy we're going to, end the state if (toucher == OrderActor) GotoState('RunningTo', 'Done'); // Handle conversations, if need be Global.Touch(toucher); } function BeginState() { StandUp(); //BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = False; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); //ResetReactions(); bStasis = True; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: if (IsOverlapping(orderActor)) Goto('Done'); MoveTarget = GetNextWaypoint(orderActor); if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) && (MoveTarget.Physics != PHYS_Falling)) { if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn')) { if (GetNextVector(orderActor, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CheckDestLoc(useLoc); } else Goto('Pause'); } else { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } if (IsOverlapping(orderActor)) Goto('Done'); else Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (orderActor.IsA('PatrolPoint')) TurnTo(Location + PatrolPoint(orderActor).lookdir); GotoState('Standing'); ContinueRun: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state DebugFollowing // // Following state used for pathnode testing // ---------------------------------------------------------------------- state DebugFollowing { function SetFall() { StartFalling('DebugFollowing', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function BeginState() { StandUp(); BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = false; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bStasis = true; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: MoveTarget = GetNextWaypoint(orderActor); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, 1.0); Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (HasNextState()) GotoNextState(); else GotoState('Standing'); ContinueRun: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state DebugPathfinding // // Following state used for pathnode testing // ---------------------------------------------------------------------- state DebugPathfinding { function SetFall() { StartFalling('DebugPathfinding', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function BeginState() { StandUp(); BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = false; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bStasis = true; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: MoveTarget = FindPathToward(orderActor); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, 1.0); Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (HasNextState()) GotoNextState(); else GotoState('Standing'); ContinueRun: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state Burning // // Whoa-oh-oh, I'm on fire. // ---------------------------------------------------------------------- state Burning { function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local name newLabel; if (health > 0) { if (enemy != instigatedBy) { SetEnemy(instigatedBy); newLabel = 'NewEnemy'; } else newLabel = 'ContinueBurn'; if ( Enemy != None ) LastSeenPos = Enemy.Location; SetNextState('Burning', newLabel); if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned')) GotoDisabledState(damageType, hitPos); } } function SetFall() { StartFalling('Burning', 'ContinueBurn'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function PickDestination() { local float magnitude; local float distribution; local int yaw, pitch; local Rotator rotator1; local NavigationPoint nav; local float dist; local NavigationPoint bestNav; local float bestDist; destPoint = None; bestNav = None; bestDist = 2000; // max distance to water // Seek out water if (bCanSwim) { nav = Level.NavigationPointList; while (nav != None) { if (nav.Region.Zone.bWaterZone) { dist = VSize(Location - nav.Location); if (dist < bestDist) { bestNav = nav; bestDist = dist; } } nav = nav.nextNavigationPoint; } } if (bestNav != None) { // It'd be nice if we could traverse all pathnodes and figure out their // distances... unfortunately, it's too slow. :( MoveTarget = FindPathToward(bestNav); if (MoveTarget != None) { destPoint = bestNav; destLoc = bestNav.Location; } } // Can't get to water -- run willy-nilly if (destPoint == None) { if (Enemy == None) { yaw = 0; pitch = 0; distribution = 0; } else { rotator1 = Rotator(Location-Enemy.Location); yaw = rotator1.Yaw; pitch = rotator1.Pitch; distribution = 0.5; } magnitude = 300*(FRand()*0.4+0.8); // 400, +/-20% if (!AIPickRandomDestination(100, magnitude, yaw, distribution, pitch, distribution, 4, FRand()*0.4+0.35, destLoc)) destLoc = Location+(VRand()*200); // we give up } } function BeginState() { StandUp(); BlockReactions(); bCanConverse = False; SetupWeapon(false, true); bStasis = False; SetDistress(true); EnemyLastSeen = 0; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bCanConverse = True; bStasis = True; } Begin: if (!bOnFire) Goto('Done'); PlayOnFireSound(); NewEnemy: Acceleration = vect(0, 0, 0); Run: if (!bOnFire) Goto('Done'); PlayPanicRunning(); PickDestination(); if (destPoint != None) { MoveToward(MoveTarget, MaxDesiredSpeed); while ((MoveTarget != None) && (MoveTarget != destPoint)) { MoveTarget = FindPathToward(destPoint); if (MoveTarget != None) MoveToward(MoveTarget, MaxDesiredSpeed); } } else MoveTo(destLoc, MaxDesiredSpeed); Goto('Run'); Done: if (IsValidEnemy(Enemy)) HandleEnemy(); else FollowOrders(); ContinueBurn: ContinueFromDoor: Goto('Run'); } // ---------------------------------------------------------------------- // state AvoidingProjectiles // // Run away from a projectile. // ---------------------------------------------------------------------- state AvoidingProjectiles { ignores EnemyNotVisible; function SetFall() { StartFalling('RunningTo', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function PickDestination(bool bGotoWatch) { local NearbyProjectileList projList; local bool bMove; local vector projVector; local rotator projRot; local int i; local int bestSlot; local float bestDist; destLoc = vect(0,0,0); destPoint = None; bMove = false; if (GetProjectileList(projList, Location) > 0) { if (IsLocationDangerous(projList, Location)) { projVector = ComputeAwayVector(projList); projRot = Rotator(projVector); if (AIDirectionReachable(Location, projRot.Yaw, projRot.Pitch, CollisionRadius+24, VSize(projVector), destLoc)) { useLoc = Location + vect(0,0,1)*BaseEyeHeight; // hack bMove = true; } } } if (bMove) GotoState('AvoidingProjectiles', 'RunAway'); else if (bGotoWatch) GotoState('AvoidingProjectiles', 'Watch'); } function BeginState() { StandUp(); Disable('AnimEnd'); bCanJump = false; SetReactions(true, true, true, true, false, true, true, true, true, true, true, true); bStasis = False; useLoc = Location + vect(0,0,1)*BaseEyeHeight + Vector(Rotation); bCanConverse = False; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); if (JumpZ > 0) bCanJump = true; ResetReactions(); bStasis = True; bCanConverse = True; } Begin: Acceleration = vect(0,0,0); PickDestination(true); RunAway: PlayTurnHead(LOOK_Forward, 1.0, 0.0001); if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); PickDestination(true); Watch: Acceleration = vect(0,0,0); PlayWaiting(); LookAtVector(useLoc, true, false, true); TurnTo(Vector(DesiredRotation)*1000+Location); sleepTime = 3.0; while (sleepTime > 0) { sleepTime -= 0.5; Sleep(0.5); PickDestination(false); } Done: if (Orders != 'AvoidingProjectiles') FollowOrders(); else GotoState('Wandering'); ContinueRun: ContinueFromDoor: PickDestination(false); Goto('Done'); } // ---------------------------------------------------------------------- // state AvoidingPawn // // Run away from an onrushing pawn (used for small, dumb critters only). // ---------------------------------------------------------------------- state AvoidingPawn { ignores EnemyNotVisible; function SetFall() { StartFalling('AvoidingPawn', 'ContinueAvoid'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function PickDestination() { local int iterations; local float magnitude; local rotator rot; local float speed; local float time; local vector newPos; local float minDist; minDist = 20; speed = VSize(Enemy.Velocity); if (speed == 0) time = 1; else time = VSize(Location - Enemy.Location)/speed; newPos = Enemy.Location + Enemy.Velocity*(time*0.98); magnitude = 100*(FRand()*0.2+0.9); // 120, +/-10% rot = Rotator(Location-newPos); iterations = 2; if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) { rot = Rotator(Location - Enemy.Location); if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) { if (speed > 0) rot = Rotator(Enemy.Velocity); else rot = Enemy.Rotation; if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) { rot.Yaw = -rot.Yaw; rot.Pitch = -rot.Pitch; if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) destLoc = Location; // we give up } } } } function BeginState() { StandUp(); bCanJump = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bStasis = True; } Begin: if (!ShouldBeStartled(Enemy)) Goto('Done'); Goto('Avoid'); ContinueFromDoor: Goto('Avoid'); Avoid: ContinueAvoid: if (!ShouldBeStartled(Enemy)) Goto('Done'); PickDestination(); if (destLoc == Location) Goto('Pause'); if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); Goto('Avoid'); Pause: PlayWaiting(); Sleep(0.0); Goto('Avoid'); Done: if (Orders != 'AvoidingPawn') FollowOrders(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state BackingOff // // Hack state used to back off when the NPC gets stuck. // ---------------------------------------------------------------------- state BackingOff { ignores EnemyNotVisible; function SetFall() { StartFalling('BackingOff', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool PickDestination() { local bool bSuccess; local float magnitude; local rotator rot; magnitude = 300; rot = Rotator(Destination-Location); bSuccess = AIPickRandomDestination(64, magnitude, rot.Yaw+32768, 0.8, -rot.Pitch, 0.8, 3, 0.9, useLoc); return bSuccess; } function bool HandleTurn(Actor Other) { GotoState('BackingOff', 'Pause'); return false; } function BeginState() { StandUp(); BlockReactions(); bStasis = False; bInTransientState = True; EnableCheckDestLoc(false); bCanJump = false; } function EndState() { EnableCheckDestLoc(false); if (JumpZ > 0) bCanJump = true; ResetReactions(); bStasis = True; bInTransientState = false; } Begin: useRot = Rotation; if (!PickDestination()) Goto('Pause'); Acceleration = vect(0,0,0); MoveAway: if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); Pause: Acceleration = vect(0,0,0); PlayWaiting(); Sleep(FRand()*2+2); Done: if (HasNextState()) GotoNextState(); else FollowOrders(); // THIS IS BAD!!! ContinueRun: ContinueFromDoor: Goto('Done'); } // ---------------------------------------------------------------------- // state OpeningDoor // // Open a door. // ---------------------------------------------------------------------- state OpeningDoor { ignores EnemyNotVisible; function SetFall() { StartFalling(NextState, NextLabel); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); if (Target == Wall) CheckOpenDoor(HitNormal, Wall); } function bool DoorEncroaches() { local bool bEncroaches; local DeusExMover dxMover; bEncroaches = true; dxMover = DeusExMover(Target); if (dxMover != None) { if (IsDoor(dxMover) && (dxMover.MoverEncroachType == ME_IgnoreWhenEncroach)) bEncroaches = false; } return bEncroaches; } function FindBackupPoint() { local vector hitNorm; local rotator rot; local vector center; local vector area; local vector relPos; local float distX, distY; local float dist; hitNorm = Normal(destLoc); rot = Rotator(hitNorm); DeusExMover(Target).ComputeMovementArea(center, area); area.X += CollisionRadius + 30; area.Y += CollisionRadius + 30; //area.Z += CollisionHeight + 30; relPos = Location - center; if ((relPos.X < area.X) && (relPos.X > -area.X) && (relPos.Y < area.Y) && (relPos.Y > -area.Y)) { // hack if (hitNorm.Y == 0) hitNorm.Y = 0.00000001; if (hitNorm.X == 0) hitNorm.X = 0.00000001; if (hitNorm.X > 0) distX = (area.X - relPos.X)/hitNorm.X; else distX = (-area.X - relPos.X)/hitNorm.X; if (hitNorm.Y > 0) distY = (area.Y - relPos.Y)/hitNorm.Y; else distY = (-area.Y - relPos.Y)/hitNorm.Y; dist = FMin(distX, distY); if (dist < 45) dist = 45; else if (dist > 700) dist = 700; // sanity check if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 40, dist, destLoc)) destLoc = Location; } else destLoc = Location; } function vector FocusDirection() { return (Vector(Rotation)*30+Location); } function StopWaiting() { GotoState('OpeningDoor', 'DoorOpened'); } function AnimEnd() { local DeusExMover moved; moved = DeusExMover(Target); if (moved != none && moved.MoveTime <= 1.5) ResetReactions(); //CyberP: this resets reactions early for fast-moving doors PlayWaiting(); } function BeginState() { StandUp(); Disable('AnimEnd'); bCanJump = false; BlockReactions(); bStasis = False; bInTransientState = True; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; ResetReactions(); bStasis = True; bInTransientState = false; } Begin: destLoc = vect(0,0,0); BeginHitNormal: Acceleration = vect(0,0,0); FindBackupPoint(); if (!DoorEncroaches()) if (!FrobDoor(Target)) Goto('DoorOpened'); PlayRunning(); StrafeTo(destLoc, FocusDirection()); if (DoorEncroaches()) if (!FrobDoor(Target)) Goto('DoorOpened'); PlayWaiting(); if (target != None && target.IsA('DeusExMover') && DeusExMover(target).MoveTime < 5.0 && DeusExMover(target).bIsDoor) //CyberP: added this block, because Sleeping for 5 secs in combat is bad. { if (enemy != None && DeusExMover(target).MoverEncroachType == ME_IgnoreWhenEncroach) { Sleep(DeusExMover(target).MoveTime*0.4); FollowOrders(); } else Sleep(DeusExMover(target).MoveTime); } else Sleep(5.0); DoorOpened: if (HasNextState()) GotoNextState(); else FollowOrders(); // THIS IS BAD!!! } // ---------------------------------------------------------------------- // state TakingHit // // React to a hit. // ---------------------------------------------------------------------- state TakingHit { ignores seeplayer, hearnoise, bump, hitwall, reacttoinjury; function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false); } function Landed(vector HitNormal) { if (Velocity.Z < -1.4 * JumpZ) MakeNoise(-0.5 * Velocity.Z/(FMax(JumpZ, 150.0))); bJustLanded = true; } function BeginState() { local DeusExPlayer player; player = DeusExPlayer(GetPlayerPawn()); StandUp(); LastPainTime = Level.TimeSeconds; LastPainAnim = AnimSequence; bInterruptState = false; BlockReactions(); bCanConverse = False; bStasis = False; SetDistress(true); if (player.PerkNamesArray[14]==1 && player.inHand != None && DeusExWeapon(player.InHand).bHandToHand) TakeHitTimer = 1.3; else if (IsA('Gray')) //CyberP: grays are formidable TakeHitTimer = 10.0; else TakeHitTimer = 2.25; //CyberP: was 2.0 EnemyReadiness = 1.0; ReactionLevel = 1.0; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bInterruptState = true; ResetReactions(); bCanConverse = True; bStasis = True; bInTransientState = false; } Begin: Acceleration = vect(0, 0, 0); FinishAnim(); if ( (Physics == PHYS_Falling) && !Region.Zone.bWaterZone ) { Acceleration = vect(0,0,0); GotoState('FallingState', 'Ducking'); } else if (HasNextState()) GotoNextState(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state RubbingEyes // // React to evil things like pepper spray. // ---------------------------------------------------------------------- state RubbingEyes { ignores seeplayer, hearnoise, bump, hitwall; function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false); } function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned')) Global.ReactToInjury(instigatedBy, damageType, hitPos); } function SetFall() { StartFalling(NextState, NextLabel); } function AnimEnd() { PlayWaiting(); } function BeginState() { StandUp(); Disable('AnimEnd'); // LastPainTime = Level.TimeSeconds; // LastPainAnim = AnimSequence; bInterruptState = false; BlockReactions(); bCanConverse = False; bStasis = False; SetupWeapon(false, true); SetDistress(true); bStunned = True; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bInterruptState = true; ResetReactions(); bCanConverse = True; bStasis = True; if (Health > 0) bStunned = False; bInTransientState = false; } Begin: Acceleration = vect(0, 0, 0); PlayTearGasSound(); RubEyes: PlayRubbingEyesStart(); FinishAnim(); PlayRubbingEyes(); Sleep(15); PlayRubbingEyesEnd(); FinishAnim(); if (HasNextState()) GotoNextState(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state Stunned // // React to being stunned. // ---------------------------------------------------------------------- state Stunned { ignores seeplayer, hearnoise, bump, hitwall; function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false); } function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned')) Global.ReactToInjury(instigatedBy, damageType, hitPos); } function SetFall() { StartFalling(NextState, NextLabel); } function AnimEnd() { PlayWaiting(); } function BeginState() { StandUp(); Disable('AnimEnd'); bInterruptState = false; BlockReactions(); bCanConverse = False; bStasis = False; SetupWeapon(false); SetDistress(true); bStunned = True; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bInterruptState = true; ResetReactions(); bCanConverse = True; bStasis = True; // if we're dead, don't reset the flag if (Health > 0) bStunned = False; bInTransientState = false; } Begin: Acceleration = vect(0, 0, 0); PlayStunned(); Sleep(15); if (HasNextState()) GotoNextState(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state Dying // // Why does the Unreal Dying state suck? // ---------------------------------------------------------------------- state Dying { ignores SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, Died, Timer, TakeDamage; event Landed(vector HitNormal) { SetPhysics(PHYS_Walking); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); if (DeathTimer > 0) { DeathTimer -= deltaSeconds; if ((DeathTimer <= 0) && (Physics == PHYS_Walking)) Acceleration = vect(0,0,0); } } function MoveFallingBody() { local Vector moveDir; local float totalTime; local float speed; local float stopTime; local int numFrames; if ((AnimRate > 0) && !IsA('Robot')) { totalTime = 1.0/AnimRate; // determine how long the anim lasts numFrames = int((1.0/(1.0-AnimLast))+0.1); // count frames (hack) // defaults moveDir = vect(0,0,0); stopTime = 0.01; ComputeFallDirection(totalTime, numFrames, moveDir, stopTime); speed = VSize(moveDir)/stopTime; // compute speed // Set variables necessary for movement when walking if (moveDir == vect(0,0,0)) Acceleration = vect(0,0,0); else Acceleration = Normal(moveDir)*AccelRate; GroundSpeed = speed; DesiredSpeed = 1.0; bIsWalking = false; DeathTimer = stopTime; } else Acceleration = vect(0,0,0); } function BeginState() { EnableCheckDestLoc(false); StandUp(); if (Weapon != None && Weapon.IsA('WeaponRifle')) //CyberP: hack: set back rifle stats { WeaponRifle(Weapon).BaseAccuracy = WeaponRifle(Weapon).default.BaseAccuracy; WeaponRifle(Weapon).maxRange = WeaponRifle(Weapon).default.MaxRange; WeaponRifle(Weapon).AccurateRange = WeaponRifle(Weapon).default.AccurateRange; WeaponRifle(Weapon).ShotTime = WeaponRifle(Weapon).default.ShotTime; } // don't do that stupid timer thing in Pawn.uc AIClearEventCallback('Futz'); AIClearEventCallback('MegaFutz'); AIClearEventCallback('Player'); AIClearEventCallback('WeaponDrawn'); AIClearEventCallback('LoudNoise'); AIClearEventCallback('WeaponFire'); AIClearEventCallback('Carcass'); AIClearEventCallback('Distress'); AIClearEventCallback('Projetile'); bInterruptState = false; BlockReactions(true); bCanConverse = False; bStasis = False; SetDistress(true); DeathTimer = 0; } Begin: WaitForLanding(); MoveFallingBody(); DesiredRotation.Pitch = 0; DesiredRotation.Roll = 0; // if we don't gib, then wait for the animation to finish if ((Health > -100) && !IsA('Robot')) { //if (!IsA('Animal')) // SetCollisionSize(0.001000,Default.CollisionHeight); //CyberP: massive hack. May have to remove (all in this block and then uncomment finishAnim()) FinishAnim(); //if (!IsA('Animal')) // SetCollisionSize(Default.CollisionRadius,Default.CollisionHeight); } //FinishAnim(); SetWeapon(None); bHidden = True; Acceleration = vect(0,0,0); SpawnCarcass(); Destroy(); } // ---------------------------------------------------------------------- // state FallingState // // Fall! // ---------------------------------------------------------------------- state FallingState { ignores Bump, Hitwall, WarnTarget, ReactToInjury; function ZoneChange(ZoneInfo newZone) { Global.ZoneChange(newZone); if (newZone.bWaterZone) GotoState('FallingState', 'Splash'); } //choose a jump velocity function AdjustJump() { local float velZ; local vector FullVel; velZ = Velocity.Z; FullVel = Normal(Velocity) * GroundSpeed; If (Location.Z > Destination.Z + CollisionHeight + 2 * MaxStepHeight) { Velocity = FullVel; Velocity.Z = velZ; Velocity = EAdjustJump(); Velocity.Z = 0; if ( VSize(Velocity) < 0.9 * GroundSpeed ) { Velocity.Z = velZ; return; } } Velocity = FullVel; Velocity.Z = JumpZ + velZ; Velocity = EAdjustJump(); } singular function BaseChange() { local float minJumpZ; Global.BaseChange(); if (Physics == PHYS_Walking) { minJumpZ = FMax(JumpZ, 150.0); bJustLanded = true; if (Health > 0) { if ((Velocity.Z < -0.8 * minJumpZ) || bUpAndOut) GotoState('FallingState', 'Landed'); else if (Velocity.Z < -0.8 * JumpZ) GotoState('FallingState', 'FastLanded'); else GotoState('FallingState', 'Done'); } } } function Landed(vector HitNormal) { local float landVol, minJumpZ; local vector legLocation; minJumpZ = FMax(JumpZ, 150.0); if ( (Velocity.Z < -0.8 * minJumpZ) || bUpAndOut) { PlayLanded(Velocity.Z); if (Velocity.Z < -700) { legLocation = Location + vect(-1,0,-1); // damage left leg TakeDamage(-0.14 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell'); legLocation = Location + vect(1,0,-1); // damage right leg TakeDamage(-0.14 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell'); legLocation = Location + vect(0,0,1); // damage torso TakeDamage(-0.04 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell'); } landVol = Velocity.Z/JumpZ; landVol = 0.005 * Mass * FMin(5, landVol * landVol); if ( !FootRegion.Zone.bWaterZone ) PlaySound(Land, SLOT_Interact, FMin(20, landVol)); } else if ( Velocity.Z < -0.8 * JumpZ ) PlayLanded(Velocity.Z); } function SetFall() { if (!bUpAndOut) GotoState('FallingState'); } function BeginState() { StandUp(); if (Enemy == None) Disable('EnemyNotVisible'); else { Disable('HearNoise'); Disable('SeePlayer'); } bInterruptState = false; bCanConverse = False; bStasis = False; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bUpAndOut = false; bInterruptState = true; bCanConverse = True; bStasis = True; bInTransientState = false; } LongFall: if ( bCanFly ) { SetPhysics(PHYS_Flying); Goto('Done'); } Sleep(0.7); PlayFalling(); if ( Velocity.Z > -150 ) //stuck { SetPhysics(PHYS_Falling); if ( Enemy != None ) Velocity = groundspeed * normal(Enemy.Location - Location); else Velocity = groundspeed * VRand(); Velocity.Z = FMax(JumpZ, 250); } Goto('LongFall'); FastLanded: FinishAnim(); TweenToWaiting(0.15); Goto('Done'); Landed: if ( !bIsPlayer ) //bots act like players Acceleration = vect(0,0,0); FinishAnim(); TweenToWaiting(0.2); if ( !bIsPlayer ) Sleep(0.08); Done: bUpAndOut = false; if (HasNextState()) GotoNextState(); else GotoState('Wandering'); Splash: bUpAndOut = false; FinishAnim(); if (HasNextState()) GotoNextState(); else GotoState('Wandering'); Begin: if (Enemy == None) Disable('EnemyNotVisible'); else { Disable('HearNoise'); Disable('SeePlayer'); } if (bUpAndOut) //water jump { if ( !bIsPlayer ) { DesiredRotation = Rotation; DesiredRotation.Pitch = 0; Velocity.Z = 440; } } else { if (Region.Zone.bWaterZone) { SetPhysics(PHYS_Swimming); GotoNextState(); } if ( !bJumpOffPawn ) AdjustJump(); else bJumpOffPawn = false; PlayFall: PlayFalling(); FinishAnim(); } if (Physics != PHYS_Falling) Goto('Done'); Sleep(2.0); Goto('LongFall'); Ducking: } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // OBSOLETE // ---------------------------------------------------------------------- function PlayHit(float Damage, vector HitLocation, name damageType, vector Momentum) { log("ERROR - PlayHit should not be called!"); } function PlayHitAnim(vector HitLocation, float Damage) { log("ERROR - PlayHitAnim should not be called!"); } function PlayDeathHit(float Damage, vector HitLocation, name damageType, vector Momentum) { log("ERROR - PlayDeathHit should not be called!"); } function PlayChallenge() { log("ERROR - PlayChallenge should not be called!"); } function JumpOffPawn() { /* Velocity += (60 + CollisionRadius) * VRand(); Velocity.Z = 180 + CollisionHeight; SetPhysics(PHYS_Falling); bJumpOffPawn = true; SetFall(); */ //log("ERROR - JumpOffPawn should not be called!"); } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- defaultproperties { Restlessness=0.500000 Wanderlust=0.500000 Cowardice=0.500000 maxRange=4000.000000 MinHealth=30.000000 RandomWandering=1.000000 bAlliancesChanged=True Orders=Wandering HomeExtent=800.000000 WalkingSpeed=0.450000 bCanBleed=True ClotPeriod=30.000000 bShowPain=True bCanSit=True bLikesNeutral=True bUseFirstSeatOnly=True bCanConverse=True bAvoidAim=True AvoidAccuracy=0.400000 bAvoidHarm=True HarmAccuracy=0.700000 CloseCombatMult=0.300000 bHateShot=True bHateInjury=True bReactPresence=True bReactProjectiles=True bEmitDistress=True RaiseAlarm=RAISEALARM_BeforeFleeing bMustFaceTarget=True FireAngle=360.000000 MaxProvocations=1 AgitationSustainTime=30.000000 AgitationDecayRate=0.050000 FearSustainTime=25.000000 FearDecayRate=0.500000 SurprisePeriod=1.000000 SightPercentage=0.500000 bHasShadow=True ShadowScale=1.000000 BaseAssHeight=-26.000000 EnemyTimeout=4.000000 bTickVisibleOnly=True bInWorld=True bHighlight=True bHokeyPokey=True InitialInventory(0)=(Count=1) InitialInventory(1)=(Count=1) InitialInventory(2)=(Count=1) InitialInventory(3)=(Count=1) InitialInventory(4)=(Count=1) InitialInventory(5)=(Count=1) InitialInventory(6)=(Count=1) InitialInventory(7)=(Count=1) bSpawnBubbles=True bWalkAround=True BurnPeriod=40.000000 DistressTimer=-1.000000 CloakThreshold=50 walkAnimMult=0.700000 runAnimMult=1.050000 bRadialBlastClamp=True bCanStrafe=True bCanWalk=True bCanSwim=True bCanOpenDoors=True bIsHuman=True bCanGlide=False AirSpeed=320.000000 AccelRate=200.000000 JumpZ=120.000000 MinHitWall=9999999827968.000000 HearingThreshold=0.150000 Skill=2.000000 AIHorizontalFov=160.000000 AspectRatio=2.300000 bForceStasis=True CollisionRadius=21.000000 BindName="ScriptedPawn" FamiliarName="DEFAULT FAMILIAR NAME - REPORT THIS AS A BUG" UnfamiliarName="DEFAULT UNFAMILIAR NAME - REPORT THIS AS A BUG" }
変更されたテキスト
ファイルを開く
//============================================================================= // ScriptedPawn. //============================================================================= class ScriptedPawn expands Pawn abstract native; // ---------------------------------------------------------------------- // Enumerations enum EDestinationType { DEST_Failure, DEST_NewLocation, DEST_SameLocation }; enum EAllianceType { ALLIANCE_Friendly, ALLIANCE_Neutral, ALLIANCE_Hostile }; enum ERaiseAlarmType { RAISEALARM_BeforeAttacking, RAISEALARM_BeforeFleeing, RAISEALARM_Never }; enum ESeekType { SEEKTYPE_None, SEEKTYPE_Sound, SEEKTYPE_Sight, SEEKTYPE_Guess, SEEKTYPE_Carcass }; enum EHitLocation { HITLOC_None, HITLOC_HeadFront, HITLOC_HeadBack, HITLOC_TorsoFront, HITLOC_TorsoBack, HITLOC_LeftLegFront, HITLOC_LeftLegBack, HITLOC_RightLegFront, HITLOC_RightLegBack, HITLOC_LeftArmFront, HITLOC_LeftArmBack, HITLOC_RightArmFront, HITLOC_RightArmBack }; enum ETurning { TURNING_None, TURNING_Left, TURNING_Right }; // ---------------------------------------------------------------------- // Structures struct WanderCandidates { var WanderPoint point; var Actor waypoint; var float dist; }; struct FleeCandidates { var HidePoint point; var Actor waypoint; var Vector location; var float score; var float dist; }; struct NearbyProjectile { var DeusExProjectile projectile; var vector location; var float dist; var float range; }; struct NearbyProjectileList { var NearbyProjectile list[8]; var vector center; }; struct InitialAllianceInfo { var() Name AllianceName; var() float AllianceLevel; var() bool bPermanent; }; struct AllianceInfoEx { var Name AllianceName; var float AllianceLevel; var float AgitationLevel; var bool bPermanent; }; struct InventoryItem { var() class<Inventory> Inventory; var() int Count; }; // ---------------------------------------------------------------------- // Variables var WanderPoint lastPoints[2]; var float Restlessness; // 0-1 var float Wanderlust; // 0-1 var float Cowardice; // 0-1 var(Combat) float BaseAccuracy; // 0-1 or thereabouts var(Combat) float MaxRange; var(Combat) float MinRange; var(Combat) float MinHealth; var(AI) float RandomWandering; // 0-1 var float sleepTime; var Actor destPoint; var Vector destLoc; var Vector useLoc; var Rotator useRot; var float seekDistance; // OBSOLETE var int SeekLevel; var ESeekType SeekType; var Pawn SeekPawn; var float CarcassTimer; var float CarcassHateTimer; // OBSOLETE var float CarcassCheckTimer; var name PotentialEnemyAlliance; var float PotentialEnemyTimer; var float BeamCheckTimer; var bool bSeekPostCombat; var bool bSeekLocation; var bool bInterruptSeek; var bool bAlliancesChanged; // set to True whenever someone changes AlliancesEx[i].AllianceLevel to indicate we must do alliance updating var bool bNoNegativeAlliances; // True means we know all alliances are currently +, allows us to skip CheckEnemyPresence's slow part var bool bSitAnywhere; var bool bSitInterpolation; var bool bStandInterpolation; var float remainingSitTime; var float remainingStandTime; var vector StandRate; var float ReloadTimer; var bool bReadyToReload; var(Pawn) class<carcass> CarcassType; // mesh to use when killed from the front // Advanced AI attributes. var(Orders) name Orders; // orders a creature is carrying out // will be initial state, plus creature will attempt // to return to this state var(Orders) name OrderTag; // tag of object referred to by orders var(Orders) name HomeTag; // tag of object to use as home base var(Orders) float HomeExtent; // extent of home base var actor OrderActor; // object referred to by orders (if applicable) var name NextAnim; // used in states with multiple, sequenced animations var float WalkingSpeed; // 0-1 var(Combat) float ProjectileSpeed; var name LastPainAnim; var float LastPainTime; var vector DesiredPrePivot; var float PrePivotTime; var vector PrePivotOffset; var bool bCanBleed; // true if an NPC can bleed var float BleedRate; // how profusely the NPC is bleeding; 0-1 var float DropCounter; // internal; used in tick() var() float ClotPeriod; // seconds it takes bleedRate to go from 1 to 0 var bool bAcceptBump; // ugly hack var bool bCanFire; // true if pawn is capable of shooting asynchronously var(AI) bool bKeepWeaponDrawn; // true if pawn should always keep weapon drawn var(AI) bool bShowPain; // true if pawn should play pain animations var(AI) bool bCanSit; // true if pawn can sit var(AI) bool bAlwaysPatrol; // true if stasis should be disabled during patrols var(AI) bool bPlayIdle; // true if pawn should fidget while he's standing var(AI) bool bLeaveAfterFleeing; // true if pawn should disappear after fleeing var(AI) bool bLikesNeutral; // true if pawn should treat neutrals as friendlies var(AI) bool bUseFirstSeatOnly; // true if only the nearest chair should be used for var(AI) bool bCower; // true if fearful pawns should cower instead of fleeing var(AI) bool bMakeFemale; // true if pawn is female var HomeBase HomeActor; // home base var Vector HomeLoc; // location of home base var Vector HomeRot; // rotation of home base var bool bUseHome; // true if home base should be used var bool bInterruptState; // true if the state can be interrupted var bool bCanConverse; // true if the pawn can converse var() bool bImportant; // true if this pawn is game-critical var() bool bInvincible; // true if this pawn cannot be killed var bool bInitialized; // true if this pawn has been initialized var(Combat) bool bAvoidAim; // avoid enemy's line of fire var(Combat) bool bAimForHead; // aim for the enemy's head var(Combat) bool bDefendHome; // defend the home base var bool bCanCrouch; // whether we should crouch when firing var bool bSeekCover; // seek cover var bool bSprint; // sprint in random directions var(Combat) bool bUseFallbackWeapons; // use fallback weapons even when others are available var float AvoidAccuracy; // how well we avoid enemy's line of fire; 0-1 var bool bAvoidHarm; // avoid painful projectiles, gas clouds, etc. var float HarmAccuracy; // how well we avoid harm; 0-1 var float CrouchRate; // how often the NPC crouches, if bCrouch enabled; 0-1 var float SprintRate; // how often the NPC randomly sprints if bSprint enabled; 0-1 var float CloseCombatMult; // multiplier for how often the NPC sprints in close combat; 0-1 // If a stimulation is enabled, it causes an NPC to hate the stimulator //var(Stimuli) bool bHateFutz; var(Stimuli) bool bHateHacking; // new var(Stimuli) bool bHateWeapon; var(Stimuli) bool bHateShot; var(Stimuli) bool bHateInjury; var(Stimuli) bool bHateIndirectInjury; // new //var(Stimuli) bool bHateGore; var(Stimuli) bool bHateCarcass; // new var(Stimuli) bool bHateDistress; //var(Stimuli) bool bHateProjectiles; // If a reaction is enabled, the NPC will react appropriately to a stimulation var(Reactions) bool bReactFutz; // new var(Reactions) bool bReactPresence; // React to the presence of an enemy (attacking) var(Reactions) bool bReactLoudNoise; // Seek the source of a loud noise (seeking) var(Reactions) bool bReactAlarm; // Seek the source of an alarm (seeking) var(Reactions) bool bReactShot; // React to a gunshot fired by an enemy (attacking) //var(Reactions) bool bReactGore; // React to gore appropriately (seeking) var(Reactions) bool bReactCarcass; // React to gore appropriately (seeking) var(Reactions) bool bReactDistress; // React to distress appropriately (attacking) var(Reactions) bool bReactProjectiles; // React to harmful projectiles appropriately // If a fear is enabled, the NPC will run away from the stimulator var(Fears) bool bFearHacking; // Run away from a hacker var(Fears) bool bFearWeapon; // Run away from a person holding a weapon var(Fears) bool bFearShot; // Run away from a person who fires a shot var(Fears) bool bFearInjury; // Run away from a person who causes injury var(Fears) bool bFearIndirectInjury; // Run away from a person who causes indirect injury var(Fears) bool bFearCarcass; // Run away from a carcass var(Fears) bool bFearDistress; // Run away from a person causing distress var(Fears) bool bFearAlarm; // Run away from the source of an alarm var(Fears) bool bFearProjectiles; // Run away from a projectile var(AI) bool bEmitDistress; // TRUE if NPC should emit distress var(AI) ERaiseAlarmType RaiseAlarm; // When to raise an alarm var bool bLookingForEnemy; // TRUE if we're actually looking for enemies var bool bLookingForLoudNoise; // TRUE if we're listening for loud noises var bool bLookingForAlarm; // TRUE if we're listening for alarms var bool bLookingForDistress; // TRUE if we're looking for signs of distress var bool bLookingForProjectiles; // TRUE if we're looking for projectiles that can harm us var bool bLookingForFutz; // TRUE if we're looking for people futzing with stuff var bool bLookingForHacking; // TRUE if we're looking for people hacking stuff var bool bLookingForShot; // TRUE if we're listening for gunshots var bool bLookingForWeapon; // TRUE if we're looking for drawn weapons var bool bLookingForCarcass; // TRUE if we're looking for carcass events var bool bLookingForInjury; // TRUE if we're looking for injury events var bool bLookingForIndirectInjury; // TRUE if we're looking for secondary injury events var bool bFacingTarget; // True if pawn is facing its target var(Combat) bool bMustFaceTarget; // True if an NPC must face his target to fire var(Combat) float FireAngle; // TOTAL angle (in degrees) in which a pawn may fire if bMustFaceTarget is false var(Combat) float FireElevation; // Max elevation distance required to attack (0=elevation doesn't matter) var(AI) int MaxProvocations; var float AgitationSustainTime; var float AgitationDecayRate; var float AgitationTimer; var float AgitationCheckTimer; var float PlayerAgitationTimer; // hack var float FearSustainTime; var float FearDecayRate; var float FearTimer; var float FearLevel; var float EnemyReadiness; var float ReactionLevel; var float SurprisePeriod; var float SightPercentage; var float CycleTimer; var float CyclePeriod; var float CycleCumulative; var Pawn CycleCandidate; var float CycleDistance; var AlarmUnit AlarmActor; var float AlarmTimer; var float WeaponTimer; var float FireTimer; var float SpecialTimer; var float CrouchTimer; var float BackpedalTimer; var bool bHasShadow; var float ShadowScale; var bool bDisappear; var bool bInTransientState; // true if the NPC is in a 3rd-tier (transient) state, like TakeHit var(Alliances) InitialAllianceInfo InitialAlliances[8]; var AllianceInfoEx AlliancesEx[16]; var bool bReverseAlliances; var(Pawn) float BaseAssHeight; var(AI) float EnemyTimeout; var float CheckPeriod; var float EnemyLastSeen; var int SeatSlot; var Seat SeatActor; var int CycleIndex; var int BodyIndex; var bool bRunningStealthy; var bool bPausing; var bool bStaring; var bool bAttacking; var bool bDistressed; var bool bStunned; var bool bSitting; var bool bDancing; var bool bCrouching; var bool bCanTurnHead; var(AI) bool bTickVisibleOnly; // Temporary? var() bool bInWorld; var vector WorldPosition; var bool bWorldCollideActors; var bool bWorldBlockActors; var bool bWorldBlockPlayers; var() bool bHighlight; // should this object not highlight when focused? var(AI) bool bHokeyPokey; var bool bConvEndState; var(Inventory) InventoryItem InitialInventory[8]; // Initial inventory items carried by the pawn var Bool bConversationEndedNormally; var Bool bInConversation; var Actor ConversationActor; // Actor currently speaking to or speaking to us var() sound WalkSound; var float swimBubbleTimer; var bool bSpawnBubbles; var bool bUseSecondaryAttack; var bool bWalkAround; var bool bClearedObstacle; var bool bEnableCheckDest; var ETurning TurnDirection; var ETurning NextDirection; var Actor ActorAvoiding; var float AvoidWallTimer; var float AvoidBumpTimer; var float ObstacleTimer; var vector LastDestLoc; var vector LastDestPoint; var int DestAttempts; var float DeathTimer; var float EnemyTimer; var float TakeHitTimer; var name ConvOrders; var name ConvOrderTag; var float BurnPeriod; var float FutzTimer; var float DistressTimer; var vector SeatLocation; var Seat SeatHack; var bool bSeatLocationValid; var bool bSeatHackUsed; var bool bBurnedToDeath; var bool bHasCloak; var bool bCloakOn; var int CloakThreshold; var float CloakEMPTimer; var float poisonTimer; // time remaining before next poison TakeDamage var int poisonCounter; // number of poison TakeDamages remaining var int poisonDamage; // damage taken from poison effect var Pawn Poisoner; // person who initiated PoisonEffect damage var Name Carcasses[4]; // list of carcasses seen var int NumCarcasses; // number of carcasses seen var float walkAnimMult; var float runAnimMult; var bool bUsingHDTP; var string HDTPMeshName; var string HDTPMeshTex[8]; //GMDX var bool bBurnedUp; //CyberP: for plasma rifle gibbing var bool bFlyer; //CyberP: for pawn knockback var bool bSetupPop; //CyberP: for headpop var int extraMult; //CyberP: variable headshot multiplier var bool bRadialBlastClamp; //CyberP: MiB radial attack var bool bCanFlare; //CyberP: AI can throw flares if conditions are met var bool bHasThrownFlare; //CyberP: has this NPC already thrown a flare? var bool bCanNade; //CyberP: AI can throw nades if conditions are met var bool bGrenadier; //CyberP: this pawn class can throw nades var bool bCommandoMelee; var bool bDefensiveStyle; //CyberP: this pawn doesn't always bum rush the player in close proximity //var bool bFrontPop; //CyberP: for headpop var int disturbanceCount; //CyberP: obsolete. var bool bAlarmStatIncrease; //CyberP: AI get a one-time stat boost if alarm is triggered var bool bHasHelmet; var bool bBiteClamp; var bool bGreaselShould; var(Filter) bool bHardcoreOnly; // CyberP: remove this pawn from world if we are not hardcore. var(AI) bool bCanPop; //CyberP: if we can pop at all. native(2102) final function ConBindEvents(); native(2105) final function bool IsValidEnemy(Pawn TestEnemy, optional bool bCheckAlliance); native(2106) final function EAllianceType GetAllianceType(Name AllianceName); native(2107) final function EAllianceType GetPawnAllianceType(Pawn QueryPawn); native(2108) final function bool HaveSeenCarcass(Name CarcassName); native(2109) final function AddCarcass(Name CarcassName); // ---------------------------------------------------------------------- // PreBeginPlay() // ---------------------------------------------------------------------- function PreBeginPlay() { local float saveBaseEyeHeight; // TODO: // // Okay, we need to save the base eye height right now becase it's // obliterated in Pawn.uc with the following: // // EyeHeight = 0.8 * CollisionHeight; //FIXME - if all baseeyeheights set right, use them // BaseEyeHeight = EyeHeight; // // This must be fixed after ECTS. saveBaseEyeHeight = BaseEyeHeight; Super.PreBeginPlay(); if (!bIsFemale) bIsFemale = bMakeFemale; //GMDX BaseEyeHeight = saveBaseEyeHeight; // create our shadow CreateShadow(); // Set our alliance SetAlliance(Alliance); // Set up callbacks UpdateReactionCallbacks(); } function UpdateHDTPsettings() { local mesh tempmesh; local texture temptex; local int i; local bool bSetHDTP; local deusexplayer P; P = deusexplayer(getplayerpawn()); if(P != none) //sigh, ok so...I guess I should've thought this through. Pawns fucked with in editor etc now also reset to defaults { bSetHDTP = P.GetHDTPSettings(self); if(bSetHDTP && !bUsingHDTP) { if(HDTPMeshname != "") { tempmesh = lodmesh(dynamicloadobject(HDTPMeshname,class'mesh',true)); if(tempmesh != none) { mesh = tempmesh; texture=none; skin=none; for(i=0;i<=7;i++) { if(HDTPMeshtex[i] != "") { temptex = texture(dynamicloadobject(HDTPMeshtex[i],class'texture',true)); if(temptex != none) multiskins[i] = temptex; } } } } bUsingHDTP=true; } else if(!bSetHDTP && bUsingHDTP) { mesh = default.mesh; texture=default.texture; skin=default.skin; for(i=0; i<=7;i++) { multiskins[i]=default.multiskins[i]; } bUsingHDTP=false; } } } // ---------------------------------------------------------------------- // PostBeginPlay() // ---------------------------------------------------------------------- function PostBeginPlay() { Super.PostBeginPlay(); //sort out HDTP settings UpdateHDTPSettings(); // Set up pain timer if (Region.Zone.bPainZone || HeadRegion.Zone.bPainZone || FootRegion.Zone.bPainZone) PainTime = 5.0; else if (HeadRegion.Zone.bWaterZone) PainTime = UnderWaterTime; // Handle holograms if ((Style != STY_Masked) && (Style != STY_Normal)) { SetSkinStyle(Style, None); SetCollision(false, false, false); KillShadow(); bHasShadow = False; } } // ---------------------------------------------------------------------- // PostPostBeginPlay() // ---------------------------------------------------------------------- function PostPostBeginPlay() { Super.PostPostBeginPlay(); // Bind any conversation events to this ScriptedPawn ConBindEvents(); } // ---------------------------------------------------------------------- // Destroyed() // ---------------------------------------------------------------------- simulated function Destroyed() { local DeusExPlayer player; // Pass a message to conPlay, if it exists in the player, that // this pawn has been destroyed. This is used to prevent // bad things from happening in converseations. player = DeusExPlayer(GetPlayerPawn()); if ((player != None) && (player.conPlay != None)) player.conPlay.ActorDestroyed(Self); Super.Destroyed(); } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // GENERAL UTILITIES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // InitializePawn() // ---------------------------------------------------------------------- function InitializePawn() { if (!bInitialized) { InitializeInventory(); InitializeAlliances(); InitializeHomeBase(); BlockReactions(); if (Alliance != '') ChangeAlly(Alliance, 1.0, true); if (!bInWorld) { // tricky bInWorld = true; LeaveWorld(); } // hack! animTimer[1] = 20.0; PlayTurnHead(LOOK_Forward, 1.0, 0.0001); bInitialized = true; } } // ---------------------------------------------------------------------- // InitializeInventory() // ---------------------------------------------------------------------- function InitializeInventory() { local int i, j; local Inventory inv; local Weapon weapons[8]; local int weaponCount; local Weapon firstWeapon; // Add initial inventory items weaponCount = 0; for (i=0; i<8; i++) { if ((InitialInventory[i].Inventory != None) && (InitialInventory[i].Count > 0)) { firstWeapon = None; for (j=0; j<InitialInventory[i].Count; j++) { inv = None; if (Class<Ammo>(InitialInventory[i].Inventory) != None) { inv = FindInventoryType(InitialInventory[i].Inventory); if (inv != None) Ammo(inv).AmmoAmount += Class<Ammo>(InitialInventory[i].Inventory).default.AmmoAmount; } if (inv == None) { inv = spawn(InitialInventory[i].Inventory, self); if (inv != None) { inv.InitialState='Idle2'; inv.GiveTo(Self); inv.SetBase(Self); if ((firstWeapon == None) && (Weapon(inv) != None)) firstWeapon = Weapon(inv); } } } if (firstWeapon != None) weapons[WeaponCount++] = firstWeapon; } } for (i=0; i<weaponCount; i++) { if ((weapons[i].AmmoType == None) && (weapons[i].AmmoName != None) && (weapons[i].AmmoName != Class'AmmoNone')) { weapons[i].AmmoType = Ammo(FindInventoryType(weapons[i].AmmoName)); if (weapons[i].AmmoType == None) { weapons[i].AmmoType = spawn(weapons[i].AmmoName); weapons[i].AmmoType.InitialState='Idle2'; weapons[i].AmmoType.GiveTo(Self); weapons[i].AmmoType.SetBase(Self); } } } SetupWeapon(false); } // ---------------------------------------------------------------------- // InitializeAlliances() // ---------------------------------------------------------------------- function InitializeAlliances() { local int i; for (i=0; i<8; i++) if (InitialAlliances[i].AllianceName != '') ChangeAlly(InitialAlliances[i].AllianceName, InitialAlliances[i].AllianceLevel, InitialAlliances[i].bPermanent); } // ---------------------------------------------------------------------- // InitializeHomeBase() // ---------------------------------------------------------------------- function InitializeHomeBase() { if (!bUseHome) { HomeActor = None; HomeLoc = Location; HomeRot = vector(Rotation); if (HomeTag == 'Start') bUseHome = true; else { HomeActor = HomeBase(FindTaggedActor(HomeTag, , Class'HomeBase')); if (HomeActor != None) { HomeLoc = HomeActor.Location; HomeRot = vector(HomeActor.Rotation); HomeExtent = HomeActor.Extent; bUseHome = true; } } HomeRot *= 100; } } // ---------------------------------------------------------------------- // AddInitialInventory() // ---------------------------------------------------------------------- function bool AddInitialInventory(class<Inventory> newInventory, optional int newCount) { local int i; if (newCount == 0) newCount = 1; for (i=0; i<8; i++) if ((InitialInventory[i].Inventory == None) && (InitialInventory[i].Count <= 0)) break; if (i < 8) { InitialInventory[i].Inventory = newInventory; InitialInventory[i].Count = newCount; return true; } else return false; } // ---------------------------------------------------------------------- // SetEnemy() // ---------------------------------------------------------------------- function bool SetEnemy(Pawn newEnemy, optional float newSeenTime, optional bool bForce) { if (bForce || IsValidEnemy(newEnemy)) { if (newEnemy != Enemy) EnemyTimer = 0; Enemy = newEnemy; EnemyLastSeen = newSeenTime; return True; } else return False; } // ---------------------------------------------------------------------- // SetState() // ---------------------------------------------------------------------- function SetState(Name stateName, optional Name labelName) { if (bInterruptState) GotoState(stateName, labelName); else SetNextState(stateName, labelName); } // ---------------------------------------------------------------------- // SetNextState() // ---------------------------------------------------------------------- function SetNextState(name newState, optional name newLabel) { if (!bInTransientState || !HasNextState()) { if ((newState != 'Conversation') && (newState != 'FirstPersonConversation')) { NextState = newState; NextLabel = newLabel; } } } // ---------------------------------------------------------------------- // ClearNextState() // ---------------------------------------------------------------------- function ClearNextState() { NextState = ''; NextLabel = ''; } // ---------------------------------------------------------------------- // HasNextState() // ---------------------------------------------------------------------- function bool HasNextState() { if ((NextState == '') || (NextState == GetStateName())) return false; else return true; } // ---------------------------------------------------------------------- // GotoNextState() // ---------------------------------------------------------------------- function GotoNextState() { local bool bSuccess; local name oldState, oldLabel; if (HasNextState()) { oldState = NextState; oldLabel = NextLabel; if (oldLabel == '') oldLabel = 'Begin'; ClearNextState(); GotoState(oldState, oldLabel); } else ClearNextState(); } // ---------------------------------------------------------------------- // SetOrders() // ---------------------------------------------------------------------- function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate) { local bool bHostile; local Pawn orderEnemy; switch (orderName) { case 'Attacking': case 'Fleeing': case 'Alerting': case 'Seeking': bHostile = true; break; default: bHostile = false; break; } if (!bHostile) { bSeatHackUsed = false; // hack! Orders = orderName; OrderTag = newOrderTag; if (bImmediate) FollowOrders(true); } else { ReactionLevel = 1.0; orderEnemy = Pawn(FindTaggedActor(newOrderTag, false, Class'Pawn')); if (orderEnemy != None) { ChangeAlly(orderEnemy.Alliance, -1, true); if (SetEnemy(orderEnemy)) SetState(orderName); } } } // ---------------------------------------------------------------------- // SetHomeBase() // ---------------------------------------------------------------------- function SetHomeBase(vector baseLocation, optional rotator baseRotator, optional float baseExtent) { local vector vectRot; if (baseExtent == 0) baseExtent = 800; HomeTag = ''; HomeActor = None; HomeLoc = baseLocation; HomeRot = vector(baseRotator)*100; HomeExtent = baseExtent; bUseHome = true; } // ---------------------------------------------------------------------- // ClearHomeBase() // ---------------------------------------------------------------------- function ClearHomeBase() { HomeTag = ''; bUseHome = false; } // ---------------------------------------------------------------------- // IsSeatValid() // ---------------------------------------------------------------------- function bool IsSeatValid(Actor checkActor) { local PlayerPawn player; local Seat checkSeat; checkSeat = Seat(checkActor); if (checkSeat == None) return false; else if (checkSeat.bDeleteMe) return false; else if (!bSitAnywhere && (VSize(checkSeat.Location-checkSeat.InitialPosition) > 70)) return false; else { player = GetPlayerPawn(); if (player != None) { if (player.CarriedDecoration == checkSeat) return false; } return true; } } // ---------------------------------------------------------------------- // SetDistress() // ---------------------------------------------------------------------- function SetDistress(bool bDistress) { bDistressed = bDistress; if (bDistress && bEmitDistress) AIStartEvent('Distress', EAITYPE_Visual); else AIEndEvent('Distress', EAITYPE_Visual); } function ShouldGreaselMelee() //CyberP: somehow gets called out of the desired state and crashes, so this prevents that. { } // ---------------------------------------------------------------------- // SetDistressTimer() // ---------------------------------------------------------------------- function SetDistressTimer() { DistressTimer = 0; } // ---------------------------------------------------------------------- // SetSeekLocation() // ---------------------------------------------------------------------- function SetSeekLocation(Pawn seekCandidate, vector newLocation, ESeekType newSeekType, optional bool bNewPostCombat) { SetEnemy(None, 0, true); SeekPawn = seekCandidate; LastSeenPos = newLocation; bSeekLocation = True; SeekType = newSeekType; if (newSeekType == SEEKTYPE_Carcass) CarcassTimer = 120.0; if (newSeekType == SEEKTYPE_Sight) SeekLevel = Max(SeekLevel, 1); else SeekLevel = Max(SeekLevel, 3); if (bNewPostCombat) bSeekPostCombat = true; } // ---------------------------------------------------------------------- // GetCarcassData() // ---------------------------------------------------------------------- function bool GetCarcassData(actor sender, out Name killer, out Name alliance, out Name CarcassName, optional bool bCheckName) { local DeusExPlayer dxPlayer; local DeusExCarcass carcass; local POVCorpse corpseItem; local bool bCares; local bool bValid; alliance = ''; killer = ''; bValid = false; dxPlayer = DeusExPlayer(sender); carcass = DeusExCarcass(sender); if (dxPlayer != None) { corpseItem = POVCorpse(dxPlayer.inHand); if (corpseItem != None) { if (corpseItem.bEmitCarcass) { alliance = corpseItem.Alliance; killer = corpseItem.KillerAlliance; CarcassName = corpseItem.CarcassName; bValid = true; } } } else if (carcass != None) { if (carcass.bEmitCarcass) { alliance = carcass.Alliance; killer = carcass.KillerAlliance; CarcassName = carcass.CarcassName; bValid = true; } } bCares = false; if (bValid && (!bCheckName || !HaveSeenCarcass(CarcassName))) { if (bFearCarcass) bCares = true; else { if (GetAllianceType(alliance) == ALLIANCE_Friendly) { if (bHateCarcass) bCares = true; else if (bReactCarcass) { if (GetAllianceType(killer) == ALLIANCE_Hostile) bCares = true; } } } } return bCares; } // ---------------------------------------------------------------------- // ReactToFutz() // ---------------------------------------------------------------------- function ReactToFutz() { if (bLookingForFutz && bReactFutz && (FutzTimer <= 0) && !bDistressed) { FutzTimer = 2.0; PlayFutzSound(); } } // ---------------------------------------------------------------------- // ReactToProjectiles() // ---------------------------------------------------------------------- function ReactToProjectiles(Actor projectileActor) { local DeusExProjectile dxProjectile; local Pawn instigator; if ((bFearProjectiles || bReactProjectiles) && bLookingForProjectiles) { dxProjectile = DeusExProjectile(projectileActor); if ((dxProjectile == None) || IsProjectileDangerous(dxProjectile)) { instigator = Pawn(projectileActor); if (instigator == None) instigator = projectileActor.Instigator; if (instigator != None) { if (bFearProjectiles) IncreaseFear(instigator, 2.0); if (SetEnemy(instigator)) { SetDistressTimer(); HandleEnemy(); } else if (bFearProjectiles && IsFearful()) { SetDistressTimer(); SetEnemy(instigator, , true); GotoState('Fleeing'); } else if (bAvoidHarm) SetState('AvoidingProjectiles'); } } } } // ---------------------------------------------------------------------- // InstigatorToPawn() // ---------------------------------------------------------------------- function Pawn InstigatorToPawn(Actor eventActor) { local Pawn pawnActor; if (Inventory(eventActor) != None) { if (Inventory(eventActor).Owner != None) eventActor = Inventory(eventActor).Owner; } else if (DeusExDecoration(eventActor) != None) eventActor = GetPlayerPawn(); else if (DeusExProjectile(eventActor) != None) eventActor = eventActor.Instigator; pawnActor = Pawn(eventActor); if (pawnActor == self) pawnActor = None; return pawnActor; } // ---------------------------------------------------------------------- // EnableShadow() // ---------------------------------------------------------------------- function EnableShadow(bool bEnable) { if (Shadow != None) { if (bEnable) Shadow.AttachDecal(32,vect(0.1,0.1,0)); else Shadow.DetachDecal(); } } // ---------------------------------------------------------------------- // CreateShadow() // ---------------------------------------------------------------------- function CreateShadow() { if (bHasShadow && bInWorld) if (Shadow == None) Shadow = Spawn(class'Shadow', Self,, Location-vect(0,0,1)*CollisionHeight, rot(16384,0,0)); } // ---------------------------------------------------------------------- // KillShadow() // ---------------------------------------------------------------------- function KillShadow() { if (Shadow != None) { Shadow.Destroy(); Shadow = None; } } // ---------------------------------------------------------------------- // EnterWorld() // ---------------------------------------------------------------------- function EnterWorld() { PutInWorld(true); } // ---------------------------------------------------------------------- // LeaveWorld() // ---------------------------------------------------------------------- function LeaveWorld() { PutInWorld(false); } // ---------------------------------------------------------------------- // PutInWorld() // ---------------------------------------------------------------------- function PutInWorld(bool bEnter) { if (bInWorld && !bEnter) { bInWorld = false; GotoState('Idle'); bHidden = true; bDetectable = false; WorldPosition = Location; bWorldCollideActors = bCollideActors; bWorldBlockActors = bBlockActors; bWorldBlockPlayers = bBlockPlayers; SetCollision(false, false, false); bCollideWorld = false; SetPhysics(PHYS_None); KillShadow(); SetLocation(Location+vect(0,0,20000)); // move it out of the way } else if (!bInWorld && bEnter) { bInWorld = true; bHidden = Default.bHidden; bDetectable = Default.bDetectable; SetLocation(WorldPosition); SetCollision(bWorldCollideActors, bWorldBlockActors, bWorldBlockPlayers); bCollideWorld = Default.bCollideWorld; SetMovementPhysics(); CreateShadow(); FollowOrders(); } } // ---------------------------------------------------------------------- // MakePawnIgnored() // ---------------------------------------------------------------------- function MakePawnIgnored(bool bNewIgnore) { if (bNewIgnore) { bIgnore = bNewIgnore; // to restore original behavior, uncomment the next line //bDetectable = !bNewIgnore; } else { bIgnore = Default.bIgnore; // to restore original behavior, uncomment the next line //bDetectable = Default.bDetectable; } } // ---------------------------------------------------------------------- // EnableCollision() [for sitting state] // ---------------------------------------------------------------------- function EnableCollision(bool bSet) { EnableShadow(bSet); if (bSet) SetCollision(Default.bCollideActors, Default.bBlockActors, Default.bBlockPlayers); else SetCollision(True, False, True); } // ---------------------------------------------------------------------- // SetBasedPawnSize() // ---------------------------------------------------------------------- function bool SetBasedPawnSize(float newRadius, float newHeight) { local float oldRadius, oldHeight; local bool bSuccess; local vector centerDelta; local float deltaEyeHeight; if (newRadius < 0) newRadius = 0; if (newHeight < 0) newHeight = 0; oldRadius = CollisionRadius; oldHeight = CollisionHeight; if ((oldRadius == newRadius) && (oldHeight == newHeight)) return true; centerDelta = vect(0, 0, 1)*(newHeight-oldHeight); deltaEyeHeight = GetDefaultCollisionHeight() - Default.BaseEyeHeight; bSuccess = false; if ((newHeight <= CollisionHeight) && (newRadius <= CollisionRadius)) // shrink { SetCollisionSize(newRadius, newHeight); if (Move(centerDelta)) bSuccess = true; else SetCollisionSize(oldRadius, oldHeight); } else { if (Move(centerDelta)) { SetCollisionSize(newRadius, newHeight); bSuccess = true; } } if (bSuccess) { PrePivotOffset = vect(0, 0, 1)*(GetDefaultCollisionHeight()-newHeight); PrePivot -= centerDelta; DesiredPrePivot -= centerDelta; BaseEyeHeight = newHeight - deltaEyeHeight; } return (bSuccess); } // ---------------------------------------------------------------------- // ResetBasedPawnSize() // ---------------------------------------------------------------------- function ResetBasedPawnSize() { SetBasedPawnSize(Default.CollisionRadius, GetDefaultCollisionHeight()); } // ---------------------------------------------------------------------- // GetDefaultCollisionHeight() // ---------------------------------------------------------------------- function float GetDefaultCollisionHeight() { return (Default.CollisionHeight-4.5); } // ---------------------------------------------------------------------- // GetCrouchHeight() // ---------------------------------------------------------------------- function float GetCrouchHeight() { return (Default.CollisionHeight*0.65); } // ---------------------------------------------------------------------- // GetSitHeight() // ---------------------------------------------------------------------- function float GetSitHeight() { return (GetDefaultCollisionHeight()+(BaseAssHeight*0.5)); } // ---------------------------------------------------------------------- // IsPointInCylinder() // ---------------------------------------------------------------------- function bool IsPointInCylinder(Actor cylinder, Vector point, optional float extraRadius, optional float extraHeight) { local bool bPointInCylinder; local float tempX, tempY, tempRad; tempX = cylinder.Location.X - point.X; tempX *= tempX; tempY = cylinder.Location.Y - point.Y; tempY *= tempY; tempRad = cylinder.CollisionRadius + extraRadius; tempRad *= tempRad; bPointInCylinder = false; if (tempX+tempY < tempRad) if (Abs(cylinder.Location.Z - point.Z) < (cylinder.CollisionHeight+extraHeight)) bPointInCylinder = true; return (bPointInCylinder); } // ---------------------------------------------------------------------- // StartFalling() // ---------------------------------------------------------------------- function StartFalling(Name resumeState, optional Name resumeLabel) { SetNextState(resumeState, resumeLabel); GotoState('FallingState'); } // ---------------------------------------------------------------------- // GetNextWaypoint() // ---------------------------------------------------------------------- function Actor GetNextWaypoint(Actor destination) { local Actor moveTarget; if (destination == None) moveTarget = None; else if (ActorReachable(destination)) moveTarget = destination; else moveTarget = FindPathToward(destination); return (moveTarget); } // ---------------------------------------------------------------------- // GetNextVector() // ---------------------------------------------------------------------- function bool GetNextVector(Actor destination, out vector outVect) { local bool bValid; local rotator rot; local float dist; local float maxDist; bValid = true; if (destination != None) { maxDist = 64; rot = Rotator(destination.Location - Location); dist = VSize(destination.Location - Location); if (dist < maxDist) outVect = destination.Location; else if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 0, maxDist, outVect)) bValid = false; } else bValid = false; return (bValid); } // ---------------------------------------------------------------------- // FindOrderActor() // ---------------------------------------------------------------------- function FindOrderActor() { if (Orders == 'Attacking') OrderActor = FindTaggedActor(OrderTag, true, Class'Pawn'); else OrderActor = FindTaggedActor(OrderTag); } // ---------------------------------------------------------------------- // FindTaggedActor() // ---------------------------------------------------------------------- function Actor FindTaggedActor(Name actorTag, optional bool bRandom, optional Class<Actor> tagClass) { local float dist; local float bestDist; local actor bestActor; local actor tempActor; bestActor = None; bestDist = 1000000; if (tagClass == None) tagClass = Class'Actor'; // if no tag, then assume the player is the target if (actorTag == '') bestActor = GetPlayerPawn(); else { foreach AllActors(tagClass, tempActor, actorTag) { if (tempActor != self) { dist = VSize(tempActor.Location - Location); if (bRandom) dist *= FRand()*0.6+0.7; // +/- 30% if ((bestActor == None) || (dist < bestDist)) { bestActor = tempActor; bestDist = dist; } } } } return bestActor; } // ---------------------------------------------------------------------- // HandleEnemy() // ---------------------------------------------------------------------- function HandleEnemy() { SetState('HandlingEnemy', 'Begin'); } // ---------------------------------------------------------------------- // HandleSighting() // ---------------------------------------------------------------------- function HandleSighting(Pawn pawnSighted) { SetSeekLocation(pawnSighted, pawnSighted.Location, SEEKTYPE_Sight); GotoState('Seeking'); } // ---------------------------------------------------------------------- // FollowOrders() // ---------------------------------------------------------------------- function FollowOrders(optional bool bDefer) { local bool bSetEnemy; local bool bUseOrderActor; if (Orders != '') { if ((Orders == 'Fleeing') || (Orders == 'Attacking')) { bSetEnemy = true; bUseOrderActor = true; } else if ((Orders == 'WaitingFor') || (Orders == 'GoingTo') || (Orders == 'RunningTo') || (Orders == 'Following') || (Orders == 'Sitting') || (Orders == 'Shadowing') || (Orders == 'DebugFollowing') || (Orders == 'DebugPathfinding')) { bSetEnemy = false; bUseOrderActor = true; } else { bSetEnemy = false; bUseOrderActor = false; } if (bUseOrderActor) { FindOrderActor(); if (bSetEnemy) SetEnemy(Pawn(OrderActor), 0, true); } if (bDefer) // hack SetState(Orders); else GotoState(Orders); } else { if (bDefer) SetState('Wandering'); else GotoState('Wandering'); } } // ---------------------------------------------------------------------- // ResetConvOrders() // ---------------------------------------------------------------------- function ResetConvOrders() { ConvOrders = ''; ConvOrderTag = ''; } // ---------------------------------------------------------------------- // GenerateTotalHealth() // // this will calculate a weighted average of all of the body parts // and put that value in the generic Health // NOTE: head and torso are both critical // ---------------------------------------------------------------------- function GenerateTotalHealth() { local float limbDamage, headDamage, torsoDamage; if (!bInvincible) { // Scoring works as follows: // Disabling the head (100 points damage) will kill you. // Disabling the torso (100 points damage) will kill you. // Disabling 2 1/2 limbs (250 points damage) will kill you. // Combinations can also do you in -- 50 points damage to the head // and 125 points damage to the limbs, for example. // Note that this formula can produce numbers less than zero, so we'll clamp our // health value... // Compute total limb damage limbDamage = 0; if (Default.HealthLegLeft > 0) limbDamage += float(Default.HealthLegLeft-HealthLegLeft)/Default.HealthLegLeft; if (Default.HealthLegRight > 0) limbDamage += float(Default.HealthLegRight-HealthLegRight)/Default.HealthLegRight; if (Default.HealthArmLeft > 0) limbDamage += float(Default.HealthArmLeft-HealthArmLeft)/Default.HealthArmLeft; if (Default.HealthArmRight > 0) limbDamage += float(Default.HealthArmRight-HealthArmRight)/Default.HealthArmRight; limbDamage *= 0.4; // 2 1/2 limbs disabled == death // Compute total head damage headDamage = 0; if (Default.HealthHead > 0) headDamage = float(Default.HealthHead-HealthHead)/Default.HealthHead; // Compute total torso damage torsoDamage = 0; if (Default.HealthTorso > 0) torsoDamage = float(Default.HealthTorso-HealthTorso)/Default.HealthTorso; // Compute total health, relative to original health level Health = FClamp(Default.Health - ((limbDamage+headDamage+torsoDamage)*Default.Health), 0.0, Default.Health); } else { // Pawn is invincible - reset health to defaults HealthHead = Default.HealthHead; HealthTorso = Default.HealthTorso; HealthArmLeft = Default.HealthArmLeft; HealthArmRight = Default.HealthArmRight; HealthLegLeft = Default.HealthLegLeft; HealthLegRight = Default.HealthLegRight; Health = Default.Health; } } // ---------------------------------------------------------------------- // UpdatePoison() // ---------------------------------------------------------------------- function UpdatePoison(float deltaTime) { if ((Health <= 0) || bDeleteMe) // no more pain -- you're already dead! return; if (poisonCounter > 0) { poisonTimer += deltaTime; if (poisonTimer >= 2.0) // pain every two seconds { poisonTimer = 0; poisonCounter--; TakeDamage(poisonDamage, Poisoner, Location, vect(0,0,0), 'PoisonEffect'); } if ((poisonCounter <= 0) || (Health <= 0) || bDeleteMe) StopPoison(); } } // ---------------------------------------------------------------------- // StartPoison() // ---------------------------------------------------------------------- function StartPoison(int Damage, Pawn newPoisoner) { if ((Health <= 0) || bDeleteMe) // no more pain -- you're already dead! return; poisonCounter = 8; // take damage no more than eight times (over 16 seconds) poisonTimer = 0; // reset pain timer Poisoner = newPoisoner; if (poisonDamage < Damage) // set damage amount poisonDamage = Damage; } // ---------------------------------------------------------------------- // StopPoison() // ---------------------------------------------------------------------- function StopPoison() { poisonCounter = 0; poisonTimer = 0; poisonDamage = 0; Poisoner = None; } // ---------------------------------------------------------------------- // HasEnemyTimedOut() // ---------------------------------------------------------------------- function bool HasEnemyTimedOut() { if (EnemyTimeout > 0) { if (EnemyLastSeen > EnemyTimeOut && bDefendHome && HomeExtent > 64) {bDefendHome = false; EnemyLastSeen *= 0.3;} //CyberP: yet another hack if (EnemyLastSeen > EnemyTimeout) return true; else return false; } else return false; } // ---------------------------------------------------------------------- // UpdateActorVisibility() // ---------------------------------------------------------------------- function UpdateActorVisibility(actor seeActor, float deltaSeconds, float checkTime, bool bCheckDir) { local bool bCanSee; CheckPeriod += deltaSeconds; if (CheckPeriod >= checkTime) { CheckPeriod = 0.0; if (seeActor != None) bCanSee = (AICanSee(seeActor, ComputeActorVisibility(seeActor), false, bCheckDir, true, true) > 0); else bCanSee = false; if (bCanSee) EnemyLastSeen = 0; else if (EnemyLastSeen <= 0) EnemyLastSeen = 0.01; } if (EnemyLastSeen > 0) EnemyLastSeen += deltaSeconds; } // ---------------------------------------------------------------------- // ComputeActorVisibility() // ---------------------------------------------------------------------- function float ComputeActorVisibility(actor seeActor) { local float visibility; if (seeActor.IsA('DeusExPlayer')) visibility = DeusExPlayer(seeActor).CalculatePlayerVisibility(self); else visibility = 1.0; return (visibility); } // ---------------------------------------------------------------------- // UpdateReactionLevel() [internal use only] // ---------------------------------------------------------------------- function UpdateReactionLevel(bool bRise, float deltaSeconds) { local float surpriseTime; // Handle surprise levels... if (bRise) { if (ReactionLevel < 1.0) { surpriseTime = SurprisePeriod; if (surpriseTime <= 0) surpriseTime = 0.00000001; ReactionLevel += deltaSeconds/surpriseTime; if (ReactionLevel > 1.0) ReactionLevel = 1.0; } } else { if (ReactionLevel > 0.0) { surpriseTime = 7.0; ReactionLevel -= deltaSeconds/surpriseTime; if (ReactionLevel <= 0.0) ReactionLevel = 0.0; } } } // ---------------------------------------------------------------------- // CheckCycle() [internal use only] // ---------------------------------------------------------------------- function Pawn CheckCycle() { local float attackPeriod; local float maxAttackPeriod; local float sustainPeriod; local float decayPeriod; local float minCutoff; local Pawn cycleEnemy; attackPeriod = 0.5; maxAttackPeriod = 4.5; sustainPeriod = 3.0; decayPeriod = 4.0; minCutoff = attackPeriod/maxAttackPeriod; cycleEnemy = None; if (CycleCumulative <= 0) // no enemies seen during this cycle { CycleTimer -= CyclePeriod; if (CycleTimer <= 0) { CycleTimer = 0; EnemyReadiness -= CyclePeriod/decayPeriod; if (EnemyReadiness < 0) EnemyReadiness = 0; } } else // I saw somebody! { CycleTimer = sustainPeriod; CycleCumulative *= 2; // hack if (CycleCumulative < minCutoff) CycleCumulative = minCutoff; else if (CycleCumulative > 1.0) CycleCumulative = 1.0; EnemyReadiness += CycleCumulative*CyclePeriod/attackPeriod; if (EnemyReadiness >= 1.0) { EnemyReadiness = 1.0; if (IsValidEnemy(CycleCandidate)) cycleEnemy = CycleCandidate; } else if (EnemyReadiness >= SightPercentage) if (IsValidEnemy(CycleCandidate)) HandleSighting(CycleCandidate); } CycleCumulative = 0; CyclePeriod = 0; CycleCandidate = None; CycleDistance = 0; return (cycleEnemy); } // ---------------------------------------------------------------------- // CheckEnemyPresence() // ---------------------------------------------------------------------- function bool CheckEnemyPresence(float deltaSeconds, bool bCheckPlayer, bool bCheckOther) { local int i; local int count; local int checked; local Pawn candidate; local float candidateDist; local DeusExPlayer playerCandidate; local bool bCanSee; local int lastCycle; local float visibility; local Pawn cycleEnemy; local bool bValid; local bool bPlayer; local float surpriseTime; local bool bValidEnemy; local bool bPotentialEnemy; local bool bCheck; bValid = false; bCanSee = false; if (bReactPresence && bLookingForEnemy && !bNoNegativeAlliances) { if (PotentialEnemyAlliance != '') bCheck = true; else { for (i=0; i<16; i++) if ((AlliancesEx[i].AllianceLevel < 0) || (AlliancesEx[i].AgitationLevel >= 1.0)) break; if (i < 16) bCheck = true; } if (bCheck) { bValid = true; CyclePeriod += deltaSeconds; count = 0; checked = 0; lastCycle = CycleIndex; foreach CycleActors(Class'Pawn', candidate, CycleIndex) { bValidEnemy = IsValidEnemy(candidate); if (!bValidEnemy && (PotentialEnemyTimer > 0)) if (PotentialEnemyAlliance == candidate.Alliance) bPotentialEnemy = true; if (bValidEnemy || bPotentialEnemy) { count++; bPlayer = candidate.IsA('PlayerPawn'); if ((bPlayer && bCheckPlayer) || (!bPlayer && bCheckOther)) { visibility = AICanSee(candidate, ComputeActorVisibility(candidate), true, true, true, true); if (visibility > 0) { if (bPotentialEnemy) // We can see the potential enemy; ergo, we hate him { IncreaseAgitation(candidate, 1.0); PotentialEnemyAlliance = ''; PotentialEnemyTimer = 0; bValidEnemy = IsValidEnemy(candidate); } if (bValidEnemy) { visibility += VisibilityThreshold; candidateDist = VSize(Location-candidate.Location); if ((CycleCandidate == None) || (CycleDistance > candidateDist)) { CycleCandidate = candidate; CycleDistance = candidateDist; } if (!bPlayer) CycleCumulative += 100000; // a bit of a hack... else CycleCumulative += visibility; } } } if (count >= 1) break; } checked++; if (checked > 20) // hacky hardcoded number break; } if (lastCycle >= CycleIndex) // have we cycled through all actors? { cycleEnemy = CheckCycle(); if (cycleEnemy != None) { SetDistressTimer(); SetEnemy(cycleEnemy, 0, true); bCanSee = true; } } } else bNoNegativeAlliances = True; } // Handle surprise levels... UpdateReactionLevel((EnemyReadiness>0)||(GetStateName()=='Seeking')||bDistressed, deltaSeconds); if (!bValid) { CycleCumulative = 0; CyclePeriod = 0; CycleCandidate = None; CycleDistance = 0; CycleTimer = 0; } return (bCanSee); } // ---------------------------------------------------------------------- // CheckBeamPresence // ---------------------------------------------------------------------- function bool CheckBeamPresence(float deltaSeconds) { local DeusExPlayer player; local Beam beamActor; local bool bReactToBeam; if (bReactPresence && bLookingForEnemy && (BeamCheckTimer <= 0) && (LastRendered() < 5.0)) { BeamCheckTimer = 1.0; player = DeusExPlayer(GetPlayerPawn()); if (player != None) { bReactToBeam = false; if (IsValidEnemy(player)) { foreach RadiusActors(Class'Beam', beamActor, 1200) { if ((beamActor.Owner == player) && (beamActor.LightType != LT_None) && (beamActor.LightBrightness > 32)) { if (VSize(beamActor.Location - Location) < (beamActor.LightRadius+1)*25) bReactToBeam = true; else { if (AICanSee(beamActor, , false, true, false, false) > 0) { if (FastTrace(beamActor.Location, Location+vect(0,0,1)*BaseEyeHeight)) bReactToBeam = true; } } } if (bReactToBeam) break; } } if (bReactToBeam) HandleSighting(player); } } } // ---------------------------------------------------------------------- // CheckCarcassPresence() // ---------------------------------------------------------------------- function bool CheckCarcassPresence(float deltaSeconds) { local Actor carcass; local Name CarcassName; local int lastCycle; local DeusExCarcass body; local DeusExPlayer player; local float visibility; local Name KillerAlliance; local Name killedAlliance; local Pawn killer; local Pawn bestKiller; local float dist; local float bestDist; local float maxCarcassDist; local int maxCarcassCount; if (bFearCarcass && !bHateCarcass && !bReactCarcass) // Major hack! maxCarcassCount = 1; else maxCarcassCount = ArrayCount(Carcasses); //if ((bHateCarcass || bReactCarcass || bFearCarcass) && bLookingForCarcass && (CarcassTimer <= 0)) if ((bHateCarcass || bReactCarcass || bFearCarcass) && (NumCarcasses < maxCarcassCount)) { maxCarcassDist = 1200; if (CarcassCheckTimer <= 0) { CarcassCheckTimer = 0.1; carcass = None; lastCycle = BodyIndex; foreach CycleActors(Class'DeusExCarcass', body, BodyIndex) { if (body.Physics != PHYS_Falling) { if (VSize(body.Location-Location) < maxCarcassDist) { if (GetCarcassData(body, KillerAlliance, killedAlliance, CarcassName, true)) { visibility = AICanSee(body, ComputeActorVisibility(body), true, true, true, true); if (visibility > 0) carcass = body; break; } } } } if (lastCycle >= BodyIndex) { if (carcass == None) { player = DeusExPlayer(GetPlayerPawn()); if (player != None) { if (VSize(player.Location-Location) < maxCarcassDist) { if (GetCarcassData(player, KillerAlliance, killedAlliance, CarcassName, true)) { visibility = AICanSee(player, ComputeActorVisibility(player), true, true, true, true); if (visibility > 0) carcass = player; } } } } } if (carcass != None) { CarcassTimer = 120; AddCarcass(CarcassName); if (bLookingForCarcass) { if (KillerAlliance == 'Player') killer = GetPlayerPawn(); else { bestKiller = None; bestDist = 0; foreach AllActors(Class'Pawn', killer) // hack { if (killer.Alliance == KillerAlliance) { dist = VSize(killer.Location - Location); if ((bestKiller == None) || (bestDist > dist)) { bestKiller = killer; bestDist = dist; } } } killer = bestKiller; } if (bHateCarcass) { PotentialEnemyAlliance = KillerAlliance; PotentialEnemyTimer = 15.0; bNoNegativeAlliances = false; } if (bFearCarcass) IncreaseFear(killer, 2.0); if (bFearCarcass && IsFearful() && !IsValidEnemy(killer)) { SetDistressTimer(); SetEnemy(killer, , true); GotoState('Fleeing'); } else { SetDistressTimer(); SetSeekLocation(killer, carcass.Location, SEEKTYPE_Carcass); HandleEnemy(); } } } } } } // ---------------------------------------------------------------------- // AddProjectileToList() // ---------------------------------------------------------------------- function AddProjectileToList(out NearbyProjectileList projList, DeusExProjectile proj, vector projPos, float dist, float range) { local int count; local int pos; local int bestPos; local float worstDist; bestPos = -1; worstDist = dist; pos = 0; while (pos < ArrayCount(projList.list)) { if (projList.list[pos].projectile == None) { bestPos = pos; break; // short-circuit loop } else { if (worstDist < projList.list[pos].dist) { worstDist = projList.list[pos].dist; bestPos = pos; } } pos++; } if (bestPos >= 0) { projList.list[bestPos].projectile = proj; projList.list[bestPos].location = projPos; projList.list[bestPos].dist = dist; projList.list[bestPos].range = range; } } // ---------------------------------------------------------------------- // IsProjectileDangerous() // ---------------------------------------------------------------------- function bool IsProjectileDangerous(DeusExProjectile projectile) { local bool bEvil; if (projectile.IsA('Cloud')) bEvil = true; else if (projectile.IsA('ThrownProjectile')) { if (projectile.IsA('SpyBot')) bEvil = false; else if ((ThrownProjectile(projectile) != None) && (ThrownProjectile(projectile).bProximityTriggered)) bEvil = false; else bEvil = true; } else bEvil = false; return (bEvil); } // ---------------------------------------------------------------------- // GetProjectileList() // ---------------------------------------------------------------------- function int GetProjectileList(out NearbyProjectileList projList, vector Location) { local float dist; local int count; local DeusExProjectile curProj; local ThrownProjectile throwProj; local Cloud cloudProj; local vector HitNormal, HitLocation; local vector extent; local vector traceEnd; local Actor hitActor; local float range; local vector pos; local float time; local float maxTime; local float elasticity; local int i; local bool bValid; for (i=0; i<ArrayCount(projList.list); i++) projList.list[i].projectile = None; projList.center = Location; maxTime = 2.0; foreach RadiusActors(Class'DeusExProjectile', curProj, 1000) { if (IsProjectileDangerous(curProj)) { throwProj = ThrownProjectile(curProj); cloudProj = Cloud(curProj); extent = vect(1,1,0)*curProj.CollisionRadius; extent.Z = curProj.CollisionHeight; range = VSize(extent); if (curProj.bExplodes) if (range < curProj.blastRadius) range = curProj.blastRadius; if (cloudProj != None) if (range < cloudProj.cloudRadius) range = cloudProj.cloudRadius+128; //CyberP:added+28 range += CollisionRadius+60; if (throwProj != None) elasticity = throwProj.Elasticity; else elasticity = 0.2; bValid = true; if (throwProj != None) if (throwProj.bProximityTriggered) // HACK!!! bValid = false; if (((curProj.Physics == PHYS_Falling) || (curProj.Physics == PHYS_Projectile) || (curProj.Physics == PHYS_None)) && bValid) { pos = curProj.Location; dist = VSize(Location - curProj.Location); AddProjectileToList(projList, curProj, pos, dist, range); if (curProj.Physics == PHYS_Projectile) { traceEnd = curProj.Location + curProj.Velocity*maxTime; hitActor = Trace(HitLocation, HitNormal, traceEnd, curProj.Location, true, extent); if (hitActor == None) pos = traceEnd; else pos = HitLocation; dist = VSize(Location - pos); AddProjectileToList(projList, curProj, pos, dist, range); } else if (curProj.Physics == PHYS_Falling) { time = ParabolicTrace(pos, curProj.Velocity, curProj.Location, true, extent, maxTime, elasticity, curProj.bBounce, 60); if (time > 0) { dist = VSize(Location - pos); AddProjectileToList(projList, curProj, pos, dist, range); } } } } } count = 0; for (i=0; i<ArrayCount(projList.list); i++) if (projList.list[i].projectile != None) count++; return (count); } // ---------------------------------------------------------------------- // IsLocationDangerous() // ---------------------------------------------------------------------- function bool IsLocationDangerous(NearbyProjectileList projList, vector Location) { local bool bDanger; local int i; local float dist; bDanger = false; for (i=0; i<ArrayCount(projList.list); i++) { if (projList.list[i].projectile == None) break; if (projList.center == Location) dist = projList.list[i].dist; else dist = VSize(projList.list[i].location - Location); if (dist < projList.list[i].range) { bDanger = true; break; } } return (bDanger); } // ---------------------------------------------------------------------- // ComputeAwayVector() // ---------------------------------------------------------------------- function vector ComputeAwayVector(NearbyProjectileList projList) { local vector awayVect; local vector tempVect; local rotator tempRot; local int i; local float dist; local int yaw; local int absYaw; local int bestYaw; local NavigationPoint navPoint; local NavigationPoint bestPoint; local float segmentDist; segmentDist = GroundSpeed*0.5; awayVect = vect(0, 0, 0); for (i=0; i<ArrayCount(projList.list); i++) { if ((projList.list[i].projectile != None) && (projList.list[i].dist < projList.list[i].range)) { tempVect = projList.list[i].location - projList.center; if (projList.list[i].dist > 0) tempVect /= projList.list[i].dist; else tempVect = VRand(); awayVect -= tempVect; } } if (awayVect != vect(0, 0, 0)) { awayVect += Normal(Velocity*vect(1,1,0))*0.9; // bias to direction already running yaw = Rotator(awayVect).Yaw; bestPoint = None; foreach ReachablePathnodes(Class'NavigationPoint', navPoint, None, dist) { tempRot = Rotator(navPoint.Location - Location); absYaw = (tempRot.Yaw - Yaw) & 65535; if (absYaw > 32767) absYaw -= 65536; absYaw = Abs(absYaw); if ((bestPoint == None) || (bestYaw > absYaw)) { bestPoint = navPoint; bestYaw = absYaw; } } if (bestPoint != None) awayVect = bestPoint.Location-Location; else { tempRot = Rotator(awayVect); tempRot.Pitch += Rand(7282)-3641; // +/- 20 degrees tempRot.Yaw += Rand(7282)-3641; // +/- 20 degrees awayVect = Vector(tempRot)*segmentDist; } } else awayVect = VRand()*segmentDist; return (awayVect); } // ---------------------------------------------------------------------- // SetupWeapon() // ---------------------------------------------------------------------- function SetupWeapon(bool bDrawWeapon, optional bool bForce) { if (bKeepWeaponDrawn && !bForce) bDrawWeapon = true; if (ShouldDropWeapon()) DropWeapon(); else if (bDrawWeapon) { // if (Weapon == None) SwitchToBestWeapon(); } else SetWeapon(None); } // ---------------------------------------------------------------------- // DropWeapon() // ---------------------------------------------------------------------- function DropWeapon() { local DeusExWeapon dxWeapon; local Weapon oldWeapon; if (Weapon != None && !Weapon.IsA('WeaponRifle')) { dxWeapon = DeusExWeapon(Weapon); if ((dxWeapon == None) || !dxWeapon.bNativeAttack) { oldWeapon = Weapon; SetWeapon(None); if (oldWeapon.IsA('WeaponAssaultGunSpider')) //CyberP: make sure these are destroyed oldWeapon.Destroy(); else oldWeapon.DropFrom(Location); } } } // ---------------------------------------------------------------------- // SetWeapon() // ---------------------------------------------------------------------- function SetWeapon(Weapon newWeapon) { if (Weapon == newWeapon) { if (Weapon != None) { if (Weapon.IsInState('DownWeapon')) Weapon.BringUp(); Weapon.SetDefaultDisplayProperties(); } if (Inventory != None) Inventory.ChangedWeapon(); PendingWeapon = None; return; } PlayWeaponSwitch(newWeapon); if (Weapon != None) { Weapon.SetDefaultDisplayProperties(); Weapon.PutDown(); } Weapon = newWeapon; if (Inventory != None) Inventory.ChangedWeapon(); if (Weapon != None) Weapon.BringUp(); PendingWeapon = None; } // ---------------------------------------------------------------------- // ReactToInjury() // ---------------------------------------------------------------------- function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local Name currentState; local bool bHateThisInjury; local bool bFearThisInjury; if ((health > 0) && (instigatedBy != None) && (bLookingForInjury || bLookingForIndirectInjury)) { currentState = GetStateName(); bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury); bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury); if (bHateThisInjury) IncreaseAgitation(instigatedBy); if (bFearThisInjury) IncreaseFear(instigatedBy, 2.0); if (SetEnemy(instigatedBy)) { SetDistressTimer(); SetNextState('HandlingEnemy'); } else if (bFearThisInjury && IsFearful() ) { SetDistressTimer(); SetEnemy(instigatedBy, , true); SetNextState('Fleeing'); } else { // if (instigatedBy.bIsPlayer) // ReactToFutz(); SetNextState(currentState); } GotoDisabledState(damageType, hitPos); } } // ---------------------------------------------------------------------- // TakeHit() // ---------------------------------------------------------------------- function TakeHit(EHitLocation hitPos) { if (hitPos != HITLOC_None) { PlayTakingHit(hitPos); GotoState('TakingHit'); } else GotoNextState(); } // ---------------------------------------------------------------------- // ComputeFallDirection() // ---------------------------------------------------------------------- function ComputeFallDirection(float totalTime, int numFrames, out vector moveDir, out float stopTime) { // Determine direction, and how long to slide if (AnimSequence == 'DeathFront') { moveDir = Vector(DesiredRotation) * Default.CollisionRadius*0.75; if (numFrames > 5) stopTime = totalTime * ((numFrames-5)/float(numFrames)); else stopTime = totalTime * 0.5; } else if (AnimSequence == 'DeathBack') { moveDir = -Vector(DesiredRotation) * Default.CollisionRadius*0.75; if (numFrames > 5) stopTime = totalTime * ((numFrames-5)/float(numFrames)); else stopTime = totalTime * 0.9; } } // ---------------------------------------------------------------------- // WillTakeStompDamage() // ---------------------------------------------------------------------- function bool WillTakeStompDamage(Actor stomper) { return true; } // ---------------------------------------------------------------------- // DrawShield() // ---------------------------------------------------------------------- function DrawShield() { local EllipseEffect shield; shield = Spawn(class'EllipseEffect', Self,, Location, Rotation); if (shield != None) shield.SetBase(Self); } // ---------------------------------------------------------------------- // StandUp() // ---------------------------------------------------------------------- function StandUp(optional bool bInstant) { local vector placeToStand; if (bSitting) { bSitInterpolation = false; bSitting = false; EnableCollision(true); SetBase(None); SetPhysics(PHYS_Falling); ResetBasedPawnSize(); if (!bInstant && (SeatActor != None) && IsOverlapping(SeatActor)) { bStandInterpolation = true; remainingStandTime = 0.3; StandRate = (Vector(SeatActor.Rotation+Rot(0, -16384, 0))*CollisionRadius) / remainingStandTime; } else StopStanding(); } if (SeatActor != None) { if (SeatActor.sittingActor[seatSlot] == self) SeatActor.sittingActor[seatSlot] = None; SeatActor = None; } if (bDancing) bDancing = false; } // ---------------------------------------------------------------------- // StopStanding() // ---------------------------------------------------------------------- function StopStanding() { if (bStandInterpolation) { bStandInterpolation = false; remainingStandTime = 0; if (Physics == PHYS_Flying) SetPhysics(PHYS_Falling); } } // ---------------------------------------------------------------------- // UpdateStanding() // ---------------------------------------------------------------------- function UpdateStanding(float deltaSeconds) { local float delta; local vector newPos; if (bStandInterpolation) { if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0))) // the bastard's walking now StopStanding(); else { if ((deltaSeconds < remainingStandTime) && (remainingStandTime > 0)) { delta = deltaSeconds; remainingStandTime -= deltaSeconds; } else { delta = remainingStandTime; StopStanding(); } newPos = StandRate*delta; Move(newPos); } } } // ---------------------------------------------------------------------- // JumpOutOfWater() // ---------------------------------------------------------------------- function JumpOutOfWater(vector jumpDir) { Falling(); Velocity = jumpDir * WaterSpeed; Acceleration = jumpDir * AccelRate; velocity.Z = 380; //set here so physics uses this for remainder of tick PlayFalling(); bUpAndOut = true; } // ---------------------------------------------------------------------- // SupportActor() // // Modified from DeusExDecoration.uc // Called when something lands on us // ---------------------------------------------------------------------- function SupportActor(Actor standingActor) { local vector newVelocity; local float angle; local float zVelocity; local float baseMass; local float standingMass; local vector damagePoint; local float damage; standingMass = FMax(1, standingActor.Mass); baseMass = FMax(1, Mass); if ((Physics == PHYS_Swimming) && Region.Zone.bWaterZone) { newVelocity = standingActor.Velocity; newVelocity *= 0.5*standingMass/baseMass; AddVelocity(newVelocity); } else { zVelocity = standingActor.Velocity.Z; damagePoint = Location + vect(0,0,1)*(CollisionHeight-1); damage = (1 - (standingMass/baseMass) * (zVelocity/100)); // Have we been stomped? if ((zVelocity*standingMass < -7500) && (damage > 0) && WillTakeStompDamage(standingActor)) TakeDamage(damage, standingActor.Instigator, damagePoint, 0.2*standingActor.Velocity, 'stomped'); if (self.IsA('Rat')) TakeDamage(6, standingActor.Instigator, damagePoint, 0.2*standingActor.Velocity, 'stomped'); } if (!self.IsA('Rat')) { // Bounce the actor off the pawn angle = FRand()*Pi*2; newVelocity.X = cos(angle); newVelocity.Y = sin(angle); newVelocity.Z = 0; newVelocity *= FRand()*25 + 25; newVelocity += standingActor.Velocity; newVelocity.Z = 50; standingActor.Velocity = newVelocity; standingActor.SetPhysics(PHYS_Falling); } } // ---------------------------------------------------------------------- // PopHead() // ---------------------------------------------------------------------- function PopHead() { } function HelmetBreak() { local int i; local PlasticFragment fragg; local vector loc; loc = Location; loc.Z += CollisionHeight+4; if (mesh != default.mesh) return; for(i=0;i<35;i++) { fragg = Spawn(class'PlasticFragment',self,,loc); if (fragg != None) { fragg.RotationRate = RotRand(); fragg.Skin = Multiskins[6]; fragg.DrawScale *= 0.3 * Frand(); fragg.Velocity = VRand()*80; } } Multiskins[6]=None; bHasHelmet=False; if (IsA('UNATCOTroop')) CarcassType = Class'DeusEx.UNATCOTroopCarcassDehelm'; else if (IsA('Soldier')) CarcassType = Class'DeusEx.SoldierCarcassDehelm'; } singular function HelmetSpawn(Vector hitLocation, int actualDamage, Pawn instigatedBy) { local int i; local GMDXUnatcoDamHelmet dh; local vector loc; loc = Location; loc.Z += CollisionHeight+1; if (mesh != default.mesh || instigatedBy == None) return; dh = Spawn(class'GMDXUnatcoDamHelmet',,,loc); if (dh != None) { dh.Velocity = Vector(instigatedBy.ViewRotation) * (170 + actualDamage); //CyberP/Totalitarian: terrible, lazy hack. dh.Velocity.Z += 50; dh.SetTimer(0.3,false); } Multiskins[6]=None; bHasHelmet=False; if (IsA('UNATCOTroop')) CarcassType = Class'DeusEx.UNATCOTroopCarcassDehelm'; else if (IsA('Soldier')) CarcassType = Class'DeusEx.SoldierCarcassDehelm'; } // ---------------------------------------------------------------------- // SpawnCarcass() // ---------------------------------------------------------------------- function Carcass SpawnCarcass() { local DeusExCarcass carc; local vector loc; local Inventory item, nextItem; local FleshFragment chunk; local BloodSpurt spur; local int i; local float size; // if we really got blown up good, gib us and don't display a carcass if ((Health < -120) && !IsA('Robot')) //CyberP: was -100 { size = (CollisionRadius + CollisionHeight) / 2; //CyberP: was 2 ExpelInventory(); //CyberP: if (size > 10.0) { spawn(class'BoneSkullBloody',,,); spawn(class'FleshFragmentSmoking'); //CyberP: not in the loop below so less spawned. spawn(class'FleshFragmentSmoking'); spawn(class'FleshFragmentSmoking'); //CyberP: not in the loop below so less spawned. spawn(class'FleshFragmentSmoking'); spawn(class'BloodDropFlying'); spawn(class'BloodDropFlying'); spawn(class'BloodDropFlying'); spawn(class'BloodDropFlying'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); Spawn(class'FleshFragmentSmall'); if (!IsA('Animal')) Spawn(class'BloodExplodeHit'); loc.X = (1-2*FRand()) * CollisionRadius; loc.Y = (1-2*FRand()) * CollisionRadius; loc.Z = (1-2*FRand()) * CollisionHeight; loc += Location; for (i=0; i<size/1.1; i++) //CyberP: was /1.2 { spawn(class'BloodDropFlying'); chunk = spawn(class'FleshFragment', None,, loc); spur = spawn(class'BloodSpurt'); if (spur!=none && i < 4) { spur.LifeSpan+=0.3; spur.DrawScale+=FRand(); } if (chunk != None) { if (bBurnedUp) { chunk.Velocity.Z = FRand() * 280 + 280; chunk.DrawScale = size / 26; if (FRand() < 0.4) { chunk.Skin=Texture'HDTPDecos.Skins.HDTPAlarmLightTex5';//Texture'Effects.Fire.Wepn_Prifle_SFX'; chunk.LightEffect=LE_FireWaver; chunk.LightBrightness=255; chunk.LightHue=80; chunk.LightSaturation=128; chunk.LightRadius=2; } if (FRand() < 0.3) chunk.bSmoking = true; } else { chunk.Velocity.Z = FRand() * 370 + 370; chunk.DrawScale = size / 22; //Cyberp: was 22 //chunk.SetCollisionSize(chunk.CollisionRadius / chunk.DrawScale, chunk.CollisionHeight / chunk.DrawScale); chunk.bFixedRotationDir = False; chunk.RotationRate = RotRand()*1000; //CyberP: was False } } } } return None; } // spawn the carcass carc = DeusExCarcass(Spawn(CarcassType)); if ( carc != None ) { if (bSetupPop) carc.bPop = True; if (bStunned) carc.bNotDead = True; carc.Initfor(self); // move it down to the floor loc = Location; loc.z -= Default.CollisionHeight; loc.z += carc.Default.CollisionHeight; carc.SetLocation(loc); carc.Velocity = Velocity; carc.Acceleration = Acceleration; // give the carcass the pawn's inventory if we aren't an animal or robot if (!IsA('Animal') && !IsA('Robot')) { if (Inventory != None) { do { item = Inventory; nextItem = item.Inventory; DeleteInventory(item); if ((DeusExWeapon(item) != None) && (DeusExWeapon(item).bNativeAttack)) item.Destroy(); else carc.AddInventory(item); item = nextItem; } until (item == None); } } } return carc; } // ---------------------------------------------------------------------- // ExpelInventory() // // G-Flex: so if the NPC is gibbed, items won't be lost // G-Flex: uses some logic from SpawnCarcass() // ---------------------------------------------------------------------- function ExpelInventory() { local Vector loc; local Inventory item, nextItem; //G-Flex: just like carcass-spawning, don't bother with animal or robot inventories if (!IsA('Animal') && !IsA('Robot')) { if (Inventory != None) { do { item = Inventory; nextItem = item.Inventory; //G-Flex: not necessary to delete items since dropping them does that //DeleteInventory(item); if ((Ammo(item) != None) || ((DeusExWeapon(item) != None) && (DeusExWeapon(item).bNativeAttack || DeusExWeapon(item).IsA('WeaponAssaultGunSpider')))) item.Destroy(); else { //G-Flex: spread them out like chunks, but a little less loc.X = (1-2*FRand()) * CollisionRadius; loc.Y = (1-2*FRand()) * CollisionRadius; loc.Z = (1-2*FRand()) * CollisionHeight; loc += Location; item.DropFrom(loc); item.Velocity += Velocity; item.Velocity += VRand() * (100.0 + (400.0 / item.Mass)); //G-Flex: get the same ammo as from the carcass or a dropped weapon if (item.IsA('DeusExWeapon')) DeusExWeapon(item).SetDroppedAmmoCount(); } item = nextItem; } until (item == None); } } } // ---------------------------------------------------------------------- // FilterDamageType() // ---------------------------------------------------------------------- function bool FilterDamageType(Pawn instigatedBy, Vector hitLocation, Vector offset, Name damageType) { // Special cases for certain damage types if (damageType == 'HalonGas') if (bOnFire) ExtinguishFire(); if (damageType == 'EMP') { //CYBERP: EMP removes cloak outright bHasCloak = False; CloakThreshold = 0; EnableCloak(False); /*CloakEMPTimer += 10; if (CloakEMPTimer > 20) CloakEMPTimer = 20; EnableCloak(bCloakOn); */ return false; } return true; } // ---------------------------------------------------------------------- // ModifyDamage() // ---------------------------------------------------------------------- function float ModifyDamage(int Damage, Pawn instigatedBy, Vector hitLocation, Vector offset, Name damageType) { local int actualDamage; local float headOffsetZ, headOffsetY, armOffset; actualDamage = Damage; // calculate our hit extents headOffsetZ = CollisionHeight * 0.7; headOffsetY = CollisionRadius * 0.3; armOffset = CollisionRadius * 0.35; // if the pawn is stunned, damage is 4X if (bStunned) actualDamage *= 8; //CyberP: override bug: now *8, hacky fix // if the pawn is hit from behind at point-blank range, he is killed instantly else if (offset.x < 0) if ((instigatedBy != None) && (VSize(instigatedBy.Location - Location) < 64)) actualDamage *= 12; //CyberP: was 10 actualDamage = Level.Game.ReduceDamage(actualDamage, DamageType, self, instigatedBy); if (ReducedDamageType == 'All') //God mode actualDamage = 0; else if (Inventory != None) //then check if carrying armor actualDamage = Inventory.ReduceDamage(actualDamage, DamageType, HitLocation); // gas, EMP and nanovirus do no damage if (damageType == 'TearGas' || damageType == 'EMP' || damageType == 'NanoVirus') actualDamage = 0; if (damageType == 'EMP') {bHasCloak = False; CloakThreshold = 0;}//CyberP: EMP just outright disables cloaking. return actualDamage; } // ---------------------------------------------------------------------- // ShieldDamage() // ---------------------------------------------------------------------- function float ShieldDamage(Name damageType) { return 1.0; } // ---------------------------------------------------------------------- // ImpartMomentum() // ---------------------------------------------------------------------- function ImpartMomentum(Vector momentum, Pawn instigatedBy) { if (Physics == PHYS_None) SetMovementPhysics(); if (Physics == PHYS_Walking) momentum.Z = 0.4 * VSize(momentum); if ( instigatedBy == self ) momentum *= 0.6; momentum = momentum/Mass; AddVelocity( momentum ); } // ---------------------------------------------------------------------- // AddVelocity() // ---------------------------------------------------------------------- function AddVelocity(Vector momentum) { if (VSize(momentum) > 0.001) Super.AddVelocity(momentum); } // ---------------------------------------------------------------------- // CanShowPain() // ---------------------------------------------------------------------- function bool CanShowPain() { if (bShowPain && (TakeHitTimer <= 0)) return true; else return false; } // ---------------------------------------------------------------------- // IsPrimaryDamageType() // ---------------------------------------------------------------------- function bool IsPrimaryDamageType(name damageType) { local bool bPrimary; switch (damageType) { case 'Exploded': case 'TearGas': case 'HalonGas': case 'PoisonGas': case 'PoisonEffect': case 'Radiation': case 'EMP': case 'Drowned': case 'NanoVirus': bPrimary = false; break; case 'Stunned': case 'KnockedOut': case 'Burned': case 'Flamed': case 'Poison': case 'Shot': case 'Sabot': default: bPrimary = true; break; } return (bPrimary); } // ---------------------------------------------------------------------- // ShouldReactToInjuryType() // ---------------------------------------------------------------------- function bool ShouldReactToInjuryType(name damageType, bool bHatePrimary, bool bHateSecondary) { local bool bIsPrimary; bIsPrimary = IsPrimaryDamageType(damageType); if ((bHatePrimary && bIsPrimary) || (bHateSecondary && !bIsPrimary)) return true; else return false; } // ---------------------------------------------------------------------- // HandleDamage() // ---------------------------------------------------------------------- function EHitLocation HandleDamage(int actualDamage, Vector hitLocation, Vector offset, name damageType, optional Pawn instigatedBy) { local EHitLocation hitPos; local float headOffsetZ, headOffsetY, armOffset; // calculate our hit extents headOffsetZ = CollisionHeight * 0.7; headOffsetY = CollisionRadius * 0.3; armOffset = CollisionRadius * 0.35; hitPos = HITLOC_None; if (actualDamage > 0) { if (offset.z > headOffsetZ) // head { if (offset.z > 41.0 && bHasHelmet && damageType == 'Shot') //offset.z > 41.0 { PlaySound(sound'ArmorRicochet',SLOT_None,,,1536); if (!IsA('MJ12Troop') && !IsA('MJ12Elite') && !IsA('UNATCOTroop') && (actualDamage >= 25 || FRand() < 0.04)) { HelmetBreak(); } else if (IsA('UNATCOTroop') && (actualDamage >= 25 || FRand() < 0.07)) { HelmetSpawn(hitLocation, actualDamage, instigatedBy); actualDamage = 0; damageType = ''; } else { actualDamage = 0; damageType = ''; } } // narrow the head region if ((Abs(offset.x) < headOffsetY) || (Abs(offset.y) < headOffsetY)) { // don't allow headshots with stunning weapons if ((damageType == 'Stunned') || (damageType == 'KnockedOut')) HealthHead -= actualDamage * 5; //CyberP: Overruled. else HealthHead -= actualDamage * (8+extraMult); //CyberP: gmdx headshot multiplier variation if (offset.x < 0.0) { hitPos = HITLOC_HeadBack; bFlyer=False; // CyberP: DO allow headshots with stunning weapons if from behind. if (((damageType == 'Stunned') || (damageType == 'KnockedOut')) && !IsInState('Attacking')) HealthHead -= actualDamage * 3; //bFrontPop=False; } else { hitPos = HITLOC_HeadFront; bFlyer=False; //bFrontPop=True; } } else // sides of head treated as torso //CyberP: hmm { if ((damageType == 'Stunned') || (damageType == 'KnockedOut')) HealthTorso -= actualDamage * 5; else HealthTorso -= actualDamage * (8+extraMult); //CyberP: lets just multiply the damage as head. if (offset.x < 0.0) { hitPos = HITLOC_TorsoBack; bFlyer=False; // CyberP: DO allow headshots with stunning weapons if from behind. if (((damageType == 'Stunned') || (damageType == 'KnockedOut')) && !IsInState('Attacking')) HealthHead -= actualDamage * 3; } else { hitPos = HITLOC_TorsoFront; bFlyer=False; } } } else if (offset.z < 0.0) // legs { if (offset.y > 0.0) { HealthLegRight -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_RightLegBack; else hitPos = HITLOC_RightLegFront; bFlyer=False; } else { HealthLegLeft -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_LeftLegBack; else hitPos = HITLOC_LeftLegFront; bFlyer=False; } // if this part is already dead, damage the adjacent part if ((HealthLegRight < 0) && (HealthLegLeft > 0)) { HealthLegLeft += HealthLegRight; HealthLegRight = 0; } else if ((HealthLegLeft < 0) && (HealthLegRight > 0)) { HealthLegRight += HealthLegLeft; HealthLegLeft = 0; } if (HealthLegLeft < 0) { HealthTorso += HealthLegLeft; HealthLegLeft = 0; } if (HealthLegRight < 0) { HealthTorso += HealthLegRight; HealthLegRight = 0; } } else // arms and torso { if (offset.y > armOffset) { HealthArmRight -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_RightArmBack; else hitPos = HITLOC_RightArmFront; bFlyer=False; } else if (offset.y < -armOffset) { HealthArmLeft -= actualDamage * 2; if (offset.x < 0.0) hitPos = HITLOC_LeftArmBack; else hitPos = HITLOC_LeftArmFront; bFlyer=False; } else { HealthTorso -= actualDamage * 3; //CyberP: was *2. Make torso a bit more vulnerable if (offset.x < 0.0) { hitPos = HITLOC_TorsoBack; bFlyer=False; } else { hitPos = HITLOC_TorsoFront; bFlyer=True; } } // if this part is already dead, damage the adjacent part if (HealthArmLeft < 0) { HealthTorso += HealthArmLeft; HealthArmLeft = 0; } if (HealthArmRight < 0) { HealthTorso += HealthArmRight; HealthArmRight = 0; } } } GenerateTotalHealth(); return hitPos; } // ---------------------------------------------------------------------- // TakeDamageBase() // ---------------------------------------------------------------------- function TakeDamageBase(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType, bool bPlayAnim) { local int actualDamage; local Vector offset; local float origHealth; local EHitLocation hitPos; local float shieldMult; local DeusExPlayer player; //CyberP: for screenflash if near gibs local float dist; //CyberP: for screenflash if near gibs local GMDXImpactSpark AST; local FleshFragmentSmall ffs; local int i; local DeusExWeapon wepa; // use the hitlocation to determine where the pawn is hit // transform the worldspace hitlocation into objectspace // in objectspace, remember X is front to back // Y is side to side, and Z is top to bottom offset = (hitLocation - Location) << Rotation; if (!CanShowPain()) bPlayAnim = false; // Prevent injury if the NPC is intangible if (!bBlockActors && !bBlockPlayers && !bCollideActors) return; // No damage + no damage type = no reaction if ((Damage <= 0) && (damageType == 'None')) return; // Block certain damage types; perform special ops on others if (!FilterDamageType(instigatedBy, hitLocation, offset, damageType)) return; // Impart momentum ImpartMomentum(momentum, instigatedBy); actualDamage = ModifyDamage(Damage, instigatedBy, hitLocation, offset, damageType); if (instigatedBy != None && instigatedBy.IsA('DeusExPlayer')) { if (DeusExPlayer(instigatedBy).bHitmarkerOn) { DeusExPlayer(instigatedBy).hitmarkerTime = 0.2; //DeusExPlayer(instigatedBy).PlaySound(sound'GMDXSFX.Weapons.HitmarkerSFX',SLOT_Interface,,,256); } if (DeusExPlayer(instigatedBy).inHand != None) { if (DeusExPlayer(instigatedBy).inHand.IsA('WeaponNanoSword') || DeusExPlayer(instigatedBy).inHand.IsA('WeaponCrowbar')) extraMult = -2; else if ((DeusExPlayer(instigatedBy).inHand.IsA('DeusExWeapon') && DeusExWeapon(DeusExPlayer(instigatedBy).inHand).ItemName == "USP.10") || DeusExPlayer(instigatedBy).inHand.IsA('WeaponSawedOffShotgun')) extraMult = 1; } } if (actualDamage > 0) { shieldMult = ShieldDamage(damageType); if (shieldMult > 0) actualDamage = Max(int(actualDamage*shieldMult), 1); else actualDamage = 0; if (shieldMult < 1.0) DrawShield(); } origHealth = Health; //log("Damage="$Damage); hitPos = HandleDamage(actualDamage, hitLocation, offset, damageType, instigatedBy); if (!bPlayAnim || (actualDamage <= 0)) hitPos = HITLOC_None; if (bCanBleed) if ((damageType != 'Stunned') && (damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'PoisonGas') && (damageType != 'Radiation') && (damageType != 'EMP') && (damageType != 'NanoVirus') && (damageType != 'Drowned') && (damageType != 'KnockedOut') && (damageType != 'Poison') && (damageType != 'PoisonEffect')) bleedRate += (origHealth-Health)/(0.3*Default.Health); // 1/3 of default health = bleed profusely if (CarriedDecoration != None) DropDecoration(); if ((actualDamage > 0) && (damageType == 'Poison')) StartPoison(Damage, instigatedBy); if (damageType == 'Stunned') { AST=Spawn(class'GMDXImpactSpark'); if (AST != None) { AST.LifeSpan=4.000000; AST.DrawScale=0.000001; AST.Velocity=vect(0,0,0); AST.AmbientSound=Sound'Ambient.Ambient.Electricity3'; AST.SoundVolume=224; AST.SoundRadius=64; AST.SoundPitch=64; } } if (IsA('MIB')) { Die = None; if (damageType == 'Poison' && GroundSpeed>default.GroundSpeed && Fatness != 130) HitSound1 = none; //CyberP: no sound for poisoned augged MiBs if (damageType != 'Poison' && actualDamage > 0 && GroundSpeed>default.GroundSpeed && Fatness != 130) HitSound1 = Sound'DeusExSounds.Generic.ArmorRicochet'; //CyberP: MiB Shield if (Fatness == 130) { if (Health > 450) { Spawn(class'SphereEffectShield'); actualDamage *= 0.3; HitSound1 = none; } else HitSound1 = Sound'DeusExSounds.Generic.ArmorRicochet'; } } if (Health <= 0) { ClearNextState(); //PlayDeathHit(actualDamage, hitLocation, damageType); if ( actualDamage > mass ) Health = -1 * actualDamage; SetEnemy(instigatedBy, 0, true); if (!bSitting && bFlyer && !IsA('Robot') && !IsA('Animal')) { player = DeusExPlayer(GetPlayerPawn()); if (DamageType == 'Shot' && (Damage >= 25 || player.inHand.IsA('WeaponAssaultShotgun') || player.inHand.IsA('WeaponSawedOffShotgun'))) //CyberP: you need to change these conditions { PlaySound(Sound'GMDXSFX.Generic.BloodSpray',SLOT_None,1.5,,1024); SetPhysics(PHYS_Flying); //flying Velocity = (Momentum*0.25)*(25*128); // damage*128 Velocity.Z = 6; //6 bFixedRotationDir = True; if (FRand() < 0.3) DropWeapon(); } } if (HealthHead < 0 && (DamageType == 'Shot' || DamageType == 'Sabot')) { Die = Sound'DeusExSounds.Generic.FleshHit1'; player = DeusExPlayer(GetPlayerPawn()); if (bCanPop && player.bHDTP_ALL == -1 && player.bDecap && (Damage >= 25 || player.inHand.IsA('WeaponAssaultShotgun') || player.inHand.IsA('WeaponSawedOffShotgun'))) //CyberP: you need to change these conditions { bSetupPop = True; spawn(class'BloodSpurt',,,HitLocation); PopHead(); if (player!=none) { dist = Abs(VSize(player.Location - Location)); if (dist < 160) { player.ClientFlash(14, vect(160,0,0)); player.bloodTime = 4.000000; } } for(i=0;i<18;i++) { ffs = spawn(Class'FleshFragmentSmall',,,hitLocation); if (ffs!=none) { ffs.Velocity.Z = FRand() * 200 + 200; ffs.Velocity.X *= 0.85; ffs.Velocity.Y *= 0.85; } spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); } } spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); spawn(Class'BloodDrop',,,hitLocation); } // gib us if we get blown up if (DamageType == 'Exploded' || (DamageType == 'Burned' && !bOnFire)) { Die = none; //CyberP: no death sounds if blown up. HitSound1 = none; //CyberP: or pain sounds HitSound2 = none; Health = -10000; if (DamageType == 'Burned') bBurnedUp = True; player = DeusExPlayer(GetPlayerPawn()); //CyberP: for screenflash if near gibs if (player!=none) //GMDX: fix log error { dist = Abs(VSize(player.Location - Location)); if (dist < 256) { player.ClientFlash(14, vect(180,0,0)); player.bloodTime = 5.000000; } } } else { Health = -1; } Died(instigatedBy, damageType, HitLocation); if ((DamageType == 'Flamed') || (DamageType == 'Burned')) { bBurnedToDeath = true; ScaleGlow *= 0.1; // blacken the corpse } else bBurnedToDeath = false; return; } // play a hit sound if (DamageType != 'Stunned') PlayTakeHitSound(actualDamage, damageType, 1); if ((DamageType == 'Flamed') && !bOnFire) CatchFire(); ReactToInjury(instigatedBy, damageType, hitPos); } // ---------------------------------------------------------------------- // IsNearHome() // ---------------------------------------------------------------------- function bool IsNearHome(vector position) { local bool bNear; bNear = true; if (bUseHome) { if (VSize(HomeLoc-position) <= HomeExtent) { if (!FastTrace(position, HomeLoc)) bNear = false; } else bNear = false; } return bNear; } // ---------------------------------------------------------------------- // IsDoor() // ---------------------------------------------------------------------- function bool IsDoor(Actor door, optional bool bWarn) { local bool bIsDoor; local DeusExMover dxMover; bIsDoor = false; dxMover = DeusExMover(door); if (dxMover != None) { if (dxMover.NumKeys > 1) { if (dxMover.bIsDoor) bIsDoor = true; /* else if (bWarn) // hack for now log("WARNING: NPC "$self$" trying to use door "$dxMover$", but bIsDoor flag is False"); */ } } return bIsDoor; } // ---------------------------------------------------------------------- // CheckOpenDoor() // ---------------------------------------------------------------------- function CheckOpenDoor(vector HitNormal, actor Door, optional name Label) { local DeusExMover dxMover; dxMover = DeusExMover(Door); if (dxMover != None) { if (bCanOpenDoors && !IsDoor(dxMover) && dxMover.bBreakable) // break glass we walk into { dxMover.TakeDamage(200, self, dxMover.Location, Velocity, 'Shot'); return; } if (((IsA('MIB') && GroundSpeed > default.GroundSpeed) || IsA('Robot')) && IsDoor(dxMover) && dxMover.bBreakable) //CyberP: super mibs & robots run straight through doors. { dxMover.TakeDamage(200, self, dxMover.Location, Velocity, 'Shot'); return; } if (dxMover.bInterpolating && (dxMover.MoverEncroachType == ME_IgnoreWhenEncroach)) return; if (bCanOpenDoors && bInterruptState && !bInTransientState && IsDoor(dxMover, true)) { if (Label == '') Label = 'Begin'; if (GetStateName() != 'OpeningDoor') SetNextState(GetStateName(), 'ContinueFromDoor'); Target = Door; destLoc = HitNormal; GotoState('OpeningDoor', 'BeginHitNormal'); } else if ((Acceleration != vect(0,0,0)) && (Physics == PHYS_Walking) && (TurnDirection == TURNING_None)) Destination = Location; } } // ---------------------------------------------------------------------- // EncroachedBy() // ---------------------------------------------------------------------- event EncroachedBy( actor Other ) { // overridden so indestructable NPCs aren't InstaGibbed by stupid movement code } // ---------------------------------------------------------------------- // EncroachedByMover() // ---------------------------------------------------------------------- function EncroachedByMover(Mover encroacher) { local DeusExMover dxMover; dxMover = DeusExMover(encroacher); if (dxMover != None) if (!dxMover.bInterpolating && IsDoor(dxMover)) FrobDoor(dxMover); } // ---------------------------------------------------------------------- // FrobDoor() // ---------------------------------------------------------------------- function bool FrobDoor(actor Target) { local DeusExMover dxMover; local DeusExMover triggerMover; local DeusExDecoration trigger; local float dist; local DeusExDecoration bestTrigger; local float bestDist; local bool bDone; bDone = false; dxMover = DeusExMover(Target); if (dxMover != None) { bestTrigger = None; bestDist = 10000; foreach AllActors(Class'DeusExDecoration', trigger) { if (dxMover.Tag == trigger.Event) { dist = VSize(Location - trigger.Location); if ((bestTrigger == None) || (bestDist > dist)) { bestTrigger = trigger; bestDist = dist; } } } if (bestTrigger != None) { foreach AllActors(Class'DeusExMover', triggerMover, dxMover.Tag) triggerMover.Trigger(bestTrigger, self); bDone = true; } else if (dxMover.bFrobbable) { if ((dxMover.WaitingPawn == None) || (dxMover.WaitingPawn == self)) { dxMover.Frob(self, None); bDone = true; } } if (bDone) dxMover.WaitingPawn = self; } return bDone; } // ---------------------------------------------------------------------- // GotoDisabledState() // ---------------------------------------------------------------------- function GotoDisabledState(name damageType, EHitLocation hitPos) { if (!bCollideActors && !bBlockActors && !bBlockPlayers) return; else if ((damageType == 'TearGas') || (damageType == 'HalonGas')) GotoState('RubbingEyes'); else if (damageType == 'Stunned') GotoState('Stunned'); else if (CanShowPain()) TakeHit(hitPos); else GotoNextState(); } // ---------------------------------------------------------------------- // PlayAnimPivot() // ---------------------------------------------------------------------- function PlayAnimPivot(name Sequence, optional float Rate, optional float TweenTime, optional vector NewPrePivot) { if (Rate == 0) Rate = 1.0; if (TweenTime == 0) TweenTime = 0.1; PlayAnim(Sequence, Rate, TweenTime); PrePivotTime = TweenTime; DesiredPrePivot = NewPrePivot + PrePivotOffset; if (PrePivotTime <= 0) PrePivot = DesiredPrePivot; } // ---------------------------------------------------------------------- // LoopAnimPivot() // ---------------------------------------------------------------------- function LoopAnimPivot(name Sequence, optional float Rate, optional float TweenTime, optional float MinRate, optional vector NewPrePivot) { if (Rate == 0) Rate = 1.0; if (TweenTime == 0) TweenTime = 0.1; LoopAnim(Sequence, Rate, TweenTime, MinRate); PrePivotTime = TweenTime; DesiredPrePivot = NewPrePivot + PrePivotOffset; if (PrePivotTime <= 0) PrePivot = DesiredPrePivot; } // ---------------------------------------------------------------------- // TweenAnimPivot() // ---------------------------------------------------------------------- function TweenAnimPivot(name Sequence, float TweenTime, optional vector NewPrePivot) { if (TweenTime == 0) TweenTime = 0.1; TweenAnim(Sequence, TweenTime); PrePivotTime = TweenTime; DesiredPrePivot = NewPrePivot + PrePivotOffset; if (PrePivotTime <= 0) PrePivot = DesiredPrePivot; } // ---------------------------------------------------------------------- // HasTwoHandedWeapon() // ---------------------------------------------------------------------- function Bool HasTwoHandedWeapon() { if ((Weapon != None) && (Weapon.Mass >= 29)) return True; else return False; } // ---------------------------------------------------------------------- // GetStyleTexture() // ---------------------------------------------------------------------- function Texture GetStyleTexture(ERenderStyle newStyle, texture oldTex, optional texture newTex) { local texture defaultTex; if (newStyle == STY_Translucent) defaultTex = Texture'BlackMaskTex'; else if (newStyle == STY_Modulated) defaultTex = Texture'GrayMaskTex'; else if (newStyle == STY_Masked) defaultTex = Texture'PinkMaskTex'; else defaultTex = Texture'BlackMaskTex'; if (oldTex == None) return defaultTex; else if (oldTex == Texture'BlackMaskTex') return Texture'BlackMaskTex'; // hack else if (oldTex == Texture'GrayMaskTex') return defaultTex; else if (oldTex == Texture'PinkMaskTex') return defaultTex; else if (newTex != None) return newTex; else return oldTex; } // ---------------------------------------------------------------------- // SetSkinStyle() // ---------------------------------------------------------------------- function SetSkinStyle(ERenderStyle newStyle, optional texture newTex, optional float newScaleGlow) { local int i; local texture curSkin; local texture oldSkin; if (newScaleGlow == 0) newScaleGlow = ScaleGlow; oldSkin = Skin; for (i=0; i<8; i++) { curSkin = GetMeshTexture(i); MultiSkins[i] = GetStyleTexture(newStyle, curSkin, newTex); } Skin = GetStyleTexture(newStyle, Skin, newTex); ScaleGlow = newScaleGlow; Style = newStyle; } // ---------------------------------------------------------------------- // ResetSkinStyle() // ---------------------------------------------------------------------- function ResetSkinStyle() { local int i; for (i=0; i<8; i++) MultiSkins[i] = Default.MultiSkins[i]; Skin = Default.Skin; ScaleGlow = Default.ScaleGlow; Style = Default.Style; if (IsA('MJ12Troop') && UnfamiliarName == "MJ12 Elite") { MultiSkins[3] = Texture'DeusExCharacters.Skins.MiscTex1'; MultiSkins[6] = Texture'DeusExCharacters.Skins.MJ12TroopTex3'; MultiSkins[7] = None; } } // ---------------------------------------------------------------------- // EnableCloak() // ---------------------------------------------------------------------- function EnableCloak(bool bEnable) // beware! called from C++ { local bool bCloaked; local SpoofedCorona cor; if (!bHasCloak || (CloakEMPTimer > 0) || (Health <= 0) || bOnFire) bEnable = false; if (bEnable && !bCloakOn && !bCloaked) { SetSkinStyle(STY_Translucent, Texture'HDTPAlarmLightTex6', 0.4); SetTimer(0.4,False); cor = Spawn(class'SpoofedCorona'); if (cor != none) cor.SetBase(self); PlaySound(Sound'CloakUp', SLOT_Pain, 0.85, ,768,1.0); AmbientGlow = 255; LightType = LT_Strobe; LightBrightness = 64; LightHue = 160; LightSaturation = 96; LightRadius = 6; KillShadow(); bCloakOn = bEnable; bCloaked = True; } else if (!bEnable && bCloakOn) { ResetSkinStyle(); CreateShadow(); LightRadius = 0; AmbientGlow = 0; bCloakOn = bEnable; bCloaked = False; if (Health > 0) PlaySound(Sound'CloakDown', SLOT_Pain, 0.85, ,768,1.0); } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // SOUND FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // PlayBodyThud() // ---------------------------------------------------------------------- function PlayBodyThud() { if(DeusExPlayer(GetPlayerPawn()).DrugEffectTimer < 0) PlaySound(sound'BodyThud', SLOT_Interact,,,, 0.5); else PlaySound(sound'BodyThud', SLOT_Interact); } // ---------------------------------------------------------------------- // RandomPitch() // // Repetitive sound pitch randomizer to help make some sounds // sound less monotonous // ---------------------------------------------------------------------- function float RandomPitch() { if(DeusExPlayer(GetPlayerPawn()).DrugEffectTimer < 0) return ((1.1 - 0.2*FRand()) * 0.5); return (1.1 - 0.2*FRand()); } // ---------------------------------------------------------------------- // Gasp() // ---------------------------------------------------------------------- function Gasp() { PlaySound(sound'MaleGasp', SLOT_Pain,,,, RandomPitch()); } // ---------------------------------------------------------------------- // PlayDyingSound() // ---------------------------------------------------------------------- function PlayDyingSound() { SetDistressTimer(); PlaySound(Die, SLOT_Pain,,,, RandomPitch()); AISendEvent('LoudNoise', EAITYPE_Audio,0.25,490); if (bEmitDistress) AISendEvent('Distress', EAITYPE_Audio,0.25,224); //CyberP: radius was 490 } // ---------------------------------------------------------------------- // PlayIdleSound() // ---------------------------------------------------------------------- function PlayIdleSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_Idle); } // ---------------------------------------------------------------------- // PlayScanningSound() // ---------------------------------------------------------------------- function PlayScanningSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_Scanning); } // ---------------------------------------------------------------------- // PlayPreAttackSearchingSound() // ---------------------------------------------------------------------- function PlayPreAttackSearchingSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) //CyberP: a bit of randomness dxPlayer.StartAIBarkConversation(self, BM_PreAttackSearching); } // ---------------------------------------------------------------------- // PlayPreAttackSightingSound() // ---------------------------------------------------------------------- function PlayPreAttackSightingSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) //CyberP: a bit of randomness dxPlayer.StartAIBarkConversation(self, BM_PreAttackSighting); } // ---------------------------------------------------------------------- // PlayPostAttackSearchingSound() // ---------------------------------------------------------------------- function PlayPostAttackSearchingSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) //CyberP: a bit of randomness dxPlayer.StartAIBarkConversation(self, BM_PostAttackSearching); } // ---------------------------------------------------------------------- // PlayTargetAcquiredSound() // ---------------------------------------------------------------------- function PlayTargetAcquiredSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_TargetAcquired); } // ---------------------------------------------------------------------- // PlayTargetLostSound() // ---------------------------------------------------------------------- function PlayTargetLostSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_TargetLost); } // ---------------------------------------------------------------------- // PlaySearchGiveUpSound() // ---------------------------------------------------------------------- function PlaySearchGiveUpSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer) && (FRand() < 0.7)) dxPlayer.StartAIBarkConversation(self, BM_SearchGiveUp); } // ---------------------------------------------------------------------- // PlayNewTargetSound() // ---------------------------------------------------------------------- function PlayNewTargetSound() { // someday... } // ---------------------------------------------------------------------- // PlayGoingForAlarmSound() // ---------------------------------------------------------------------- function PlayGoingForAlarmSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer) && (FRand() < 0.7)) dxPlayer.StartAIBarkConversation(self, BM_GoingForAlarm); } // ---------------------------------------------------------------------- // PlayOutOfAmmoSound() // ---------------------------------------------------------------------- function PlayOutOfAmmoSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_OutOfAmmo); } // ---------------------------------------------------------------------- // PlayCriticalDamageSound() // ---------------------------------------------------------------------- function PlayCriticalDamageSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_CriticalDamage); } // ---------------------------------------------------------------------- // PlayAreaSecureSound() // ---------------------------------------------------------------------- function PlayAreaSecureSound() { local DeusExPlayer dxPlayer; // Should we do a player check here? dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer) && (FRand() < 0.7)) //CyberP: cutting down on pawns barking same lines dxPlayer.StartAIBarkConversation(self, BM_AreaSecure); } // ---------------------------------------------------------------------- // PlayFutzSound() // ---------------------------------------------------------------------- function PlayFutzSound() { local DeusExPlayer dxPlayer; local Name conName; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) { if (dxPlayer.barkManager != None) { conName = dxPlayer.barkManager.BuildBarkName(self, BM_Futz); dxPlayer.StartConversationByName(conName, self, !bInterruptState); } // dxPlayer.StartAIBarkConversation(self, BM_Futz); } } // ---------------------------------------------------------------------- // PlayOnFireSound() // ---------------------------------------------------------------------- function PlayOnFireSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_OnFire); } // ---------------------------------------------------------------------- // PlayTearGasSound() // ---------------------------------------------------------------------- function PlayTearGasSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) dxPlayer.StartAIBarkConversation(self, BM_TearGas); } // ---------------------------------------------------------------------- // PlayCarcassSound() // ---------------------------------------------------------------------- function PlayCarcassSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (SeekPawn == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_Gore); } // ---------------------------------------------------------------------- // PlaySurpriseSound() // ---------------------------------------------------------------------- function PlaySurpriseSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_Surprise); } // ---------------------------------------------------------------------- // PlayAllianceHostileSound() // ---------------------------------------------------------------------- function PlayAllianceHostileSound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_AllianceHostile); } // ---------------------------------------------------------------------- // PlayAllianceFriendlySound() // ---------------------------------------------------------------------- function PlayAllianceFriendlySound() { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if ((dxPlayer != None) && (Enemy == dxPlayer)) dxPlayer.StartAIBarkConversation(self, BM_AllianceFriendly); } // ---------------------------------------------------------------------- // PlayTakeHitSound() // ---------------------------------------------------------------------- function PlayTakeHitSound(int Damage, name damageType, int Mult) { local Sound hitSound; local float volume; if (Level.TimeSeconds - LastPainSound < 0.33) //CyberP: modded return; if (Damage <= 0) return; LastPainSound = Level.TimeSeconds; if (FRand() < 0.15) hitSound = HitSound2; else hitSound = HitSound1; //CyberP: more lax conditions for hitsound2 volume = FMax(Mult*TransientSoundVolume, Mult*2.0); SetDistressTimer(); if (damageType != 'Exploded') PlaySound(hitSound, SLOT_Pain, volume,,, RandomPitch()); if ((hitSound != None) && bEmitDistress) AISendEvent('Distress', EAITYPE_Audio, volume*0.75); //CyberP: *0.75 added } // ---------------------------------------------------------------------- // GetFloorMaterial() // // Gets the name of the texture group that we are standing on // ---------------------------------------------------------------------- function name GetFloorMaterial() { local vector EndTrace, HitLocation, HitNormal; local actor target; local int texFlags; local name texName, texGroup; // trace down to our feet EndTrace = Location - CollisionHeight * 2 * vect(0,0,1); foreach TraceTexture(class'Actor', target, texName, texGroup, texFlags, HitLocation, HitNormal, EndTrace) { if ((target == Level) || target.IsA('Mover')) break; } return texGroup; } // ---------------------------------------------------------------------- // PlayFootStep() // // Plays footstep sounds based on the texture group // (yes, I know this looks nasty -- I'll have to figure out a cleaner way to do this) // ---------------------------------------------------------------------- function PlayFootStep() { local Sound stepSound; local float rnd; local name mat; local float speedFactor, massFactor; local float volume, pitch, range; local float radius, maxRadius; local float volumeMultiplier; local DeusExPlayer dxPlayer; local float shakeRadius, shakeMagnitude; local float playerDist; rnd = FRand(); mat = GetFloorMaterial(); volumeMultiplier = 1.0; if (WalkSound == None) { if (FootRegion.Zone.bWaterZone) { if (rnd < 0.33) stepSound = Sound'WaterStep1'; else if (rnd < 0.66) stepSound = Sound'WaterStep2'; else stepSound = Sound'WaterStep3'; } else { switch(mat) { case 'Textile': case 'Paper': volumeMultiplier = 0.6; if (rnd < 0.25) stepSound = Sound'CarpetStep1'; else if (rnd < 0.5) stepSound = Sound'CarpetStep2'; else if (rnd < 0.75) stepSound = Sound'CarpetStep3'; else stepSound = Sound'CarpetStep4'; break; case 'Earth': volumeMultiplier = 0.8; if (rnd < 0.25) stepSound = Sound'DIRT1'; else if (rnd < 0.5) stepSound = Sound'DIRT2'; else if (rnd < 0.75) stepSound = Sound'DIRT3'; else stepSound = Sound'DIRT4'; break; case 'Foliage': volumeMultiplier = 0.7; if (rnd < 0.25) stepSound = Sound'GrassStep1'; else if (rnd < 0.5) stepSound = Sound'GrassStep2'; else if (rnd < 0.75) stepSound = Sound'GrassStep3'; else stepSound = Sound'GrassStep4'; break; case 'Metal': volumeMultiplier = 0.9; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer.bThisMission && dxPlayer != none) { if (rnd < 0.25) stepSound = Sound'GRATE1'; else if (rnd < 0.5) stepSound = Sound'GRATE2'; else if (rnd < 0.75) stepSound = Sound'GRATE3'; else stepSound = Sound'GRATE4'; } else { if (rnd < 0.25) stepSound = Sound'MetalStep1'; else if (rnd < 0.5) stepSound = Sound'MetalStep2'; else if (rnd < 0.75) stepSound = Sound'MetalStep3'; else stepSound = Sound'MetalStep4'; } break; case 'Ladder': volumeMultiplier = 1.0; if (rnd < 0.25) stepSound = Sound'GRATE1'; else if (rnd < 0.5) stepSound = Sound'GRATE2'; else if (rnd < 0.75) stepSound = Sound'GRATE3'; else stepSound = Sound'GRATE4'; break; case 'Glass': volumeMultiplier = 0.7; if (rnd < 0.25) stepSound = Sound'GLASS1'; else if (rnd < 0.5) stepSound = Sound'GLASS2'; else if (rnd < 0.75) stepSound = Sound'GLASS3'; else stepSound = Sound'GLASS4'; break; case 'Ceramic': case 'Tiles': volumeMultiplier = 0.75; if (rnd < 0.25) stepSound = Sound'TileStep1'; else if (rnd < 0.5) stepSound = Sound'TileStep2'; else if (rnd < 0.75) stepSound = Sound'TileStep3'; else stepSound = Sound'TileStep4'; break; case 'Wood': volumeMultiplier = 0.8; if (rnd < 0.25) stepSound = Sound'WoodStep1'; else if (rnd < 0.5) stepSound = Sound'WoodStep2'; else if (rnd < 0.75) stepSound = Sound'WoodStep3'; else stepSound = Sound'WoodStep4'; break; case 'Stucco': volumeMultiplier = 0.7; if (rnd < 0.25) stepSound = Sound'CARDB1'; else if (rnd < 0.5) stepSound = Sound'CARDB2'; else if (rnd < 0.75) stepSound = Sound'CARDB3'; else stepSound = Sound'CARDB4'; break; case 'Brick': case 'Concrete': volumeMultiplier = 0.95; if (rnd < 0.25) stepSound = Sound'STEP1'; else if (rnd < 0.5) stepSound = Sound'STEP2'; else if (rnd < 0.75) stepSound = Sound'STEP3'; else stepSound = Sound'STEP4'; break; case 'Stone': default: volumeMultiplier = 0.8; if (rnd < 0.25) stepSound = Sound'StoneStep1'; else if (rnd < 0.5) stepSound = Sound'StoneStep2'; else if (rnd < 0.75) stepSound = Sound'StoneStep3'; else stepSound = Sound'StoneStep4'; break; } } } else stepSound = WalkSound; // compute sound volume, range and pitch, based on mass and speed speedFactor = VSize(Velocity)/120.0; massFactor = Mass/150.0; radius = 768.0; maxRadius = 2048.0; // volume = (speedFactor+0.2)*massFactor; // volume = (speedFactor+0.7)*massFactor; volume = massFactor*1.5; range = radius * volume; pitch = (volume+0.5); volume = 1.0; range = FClamp(range, 0.01, maxRadius); pitch = FClamp(pitch, 1.0, 1.5); if(DeusExPlayer(GetPlayerPawn()).DrugEffectTimer < 0) pitch *= 0.5; // play the sound and send an AI event PlaySound(stepSound, SLOT_Interact, volume, , range, pitch); AISendEvent('LoudNoise', EAITYPE_Audio, volume*volumeMultiplier, range*volumeMultiplier); // Shake the camera when heavy things tread if (Mass > 400) { radius = 1536.0; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) { playerDist = DistanceFromPlayer; shakeRadius = FClamp((Mass-400)/300, 0, 1.3) * (range*0.6); //CyberP: modded all this to make milbots more scary shakeMagnitude = FClamp((Mass-400)/1600, 0, 1.3); shakeMagnitude = FClamp(1.0-(playerDist/shakeRadius), 0, 1.3) * shakeMagnitude; if (shakeMagnitude > 0) dxPlayer.JoltView(shakeMagnitude); } } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // ANIMATION CALLS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // GetSwimPivot() // ---------------------------------------------------------------------- function vector GetSwimPivot() { // THIS IS A HIDEOUS, UGLY, MASSIVELY EVIL HACK!!!! return (vect(0,0,1)*CollisionHeight*0.65); } // ---------------------------------------------------------------------- // GetWalkingSpeed() // ---------------------------------------------------------------------- function float GetWalkingSpeed() { if (Physics == PHYS_Swimming) return MaxDesiredSpeed; else return WalkingSpeed; } // ---------------------------------------------------------------------- // PlayTurnHead() // ---------------------------------------------------------------------- function bool PlayTurnHead(ELookDirection newLookDir, float rate, float tweentime) { if (bCanTurnHead) { if (Super.PlayTurnHead(newLookDir, rate, tweentime)) { AIAddViewRotation = rot(0,0,0); // default switch (newLookDir) { case LOOK_Left: AIAddViewRotation = rot(0,-5461,0); // 30 degrees left break; case LOOK_Right: AIAddViewRotation = rot(0,5461,0); // 30 degrees right break; case LOOK_Up: AIAddViewRotation = rot(5461,0,0); // 30 degrees up break; case LOOK_Down: AIAddViewRotation = rot(-5461,0,0); // 30 degrees down break; case LOOK_Forward: AIAddViewRotation = rot(0,0,0); // 0 degrees break; } } else return false; } else return false; } // ---------------------------------------------------------------------- // PlayRunningAndFiring() // ---------------------------------------------------------------------- function PlayRunningAndFiring() { local DeusExWeapon W; local vector v1, v2; local float dotp; bIsWalking = FALSE; W = DeusExWeapon(Weapon); if (W != None) { if (Region.Zone.bWaterZone) { if (W.bHandToHand) LoopAnimPivot('Tread',,0.1,,GetSwimPivot()); else LoopAnimPivot('TreadShoot',,0.1,,GetSwimPivot()); } else { if (W.bHandToHand) LoopAnimPivot('Run',runAnimMult,0.1); else { v1 = Normal((Enemy.Location - Location)*vect(1,1,0)); if (destPoint != None) v2 = Normal((destPoint.Location - Location)*vect(1,1,0)); else v2 = Normal((destLoc - Location)*vect(1,1,0)); dotp = Abs(v1 dot v2); if (dotp < 0.70710678) // running sideways { if (HasTwoHandedWeapon()) LoopAnimPivot('Strafe2H',runAnimMult,0.1); else LoopAnimPivot('Strafe',runAnimMult,0.1); } else { if (HasTwoHandedWeapon()) LoopAnimPivot('RunShoot2H',runAnimMult,0.1); else LoopAnimPivot('RunShoot',runAnimMult,0.1); } } } } } // ---------------------------------------------------------------------- // PlayReloadBegin() // ---------------------------------------------------------------------- function PlayReloadBegin() { PlayAnimPivot('ReloadBegin',, 0.1); } // ---------------------------------------------------------------------- // PlayReload() // ---------------------------------------------------------------------- function PlayReload() { LoopAnimPivot('Reload',,0.2); } // ---------------------------------------------------------------------- // PlayReloadEnd() // ---------------------------------------------------------------------- function PlayReloadEnd() { PlayAnimPivot('ReloadEnd',, 0.1); } // ---------------------------------------------------------------------- // TweenToShoot() // ---------------------------------------------------------------------- function TweenToShoot(float tweentime) { if (Region.Zone.bWaterZone) TweenAnimPivot('TreadShoot', tweentime, GetSwimPivot()); else if (!bCrouching) { if (!IsWeaponReloading()) { if (HasTwoHandedWeapon()) TweenAnimPivot('Shoot2H', tweentime); else TweenAnimPivot('Shoot', tweentime); } else PlayReload(); } } // ---------------------------------------------------------------------- // PlayShoot() // ---------------------------------------------------------------------- function PlayShoot() { if (Region.Zone.bWaterZone) PlayAnimPivot('TreadShoot', , 0, GetSwimPivot()); else { if (HasTwoHandedWeapon()) PlayAnimPivot('Shoot2H', , 0); else PlayAnimPivot('Shoot', , 0); } } // ---------------------------------------------------------------------- // TweenToCrouchShoot() // ---------------------------------------------------------------------- function TweenToCrouchShoot(float tweentime) { if (Region.Zone.bWaterZone) TweenAnimPivot('TreadShoot', tweentime, GetSwimPivot()); else TweenAnimPivot('CrouchShoot', tweentime); } // ---------------------------------------------------------------------- // PlayCrouchShoot() // ---------------------------------------------------------------------- function PlayCrouchShoot() { if (Region.Zone.bWaterZone) PlayAnimPivot('TreadShoot', , 0, GetSwimPivot()); else PlayAnimPivot('CrouchShoot', , 0); } // ---------------------------------------------------------------------- // TweenToAttack() // ---------------------------------------------------------------------- function TweenToAttack(float tweentime) { if (Region.Zone.bWaterZone) TweenAnimPivot('Tread', tweentime, GetSwimPivot()); else { if (bUseSecondaryAttack) TweenAnimPivot('AttackSide', tweentime); else TweenAnimPivot('Attack', tweentime); } } // ---------------------------------------------------------------------- // PlayAttack() // ---------------------------------------------------------------------- function PlayAttack() { local DeusExWeapon W; W = DeusExWeapon(Weapon); if (Region.Zone.bWaterZone) PlayAnimPivot('Tread',,,GetSwimPivot()); else { if (bUseSecondaryAttack) PlayAnimPivot('AttackSide',1.25,0.1); //CyberP: added 1.2, same as below else if (W != None && W.bHandToHand) PlayAnimPivot('Attack',1.2,0.1); //CyberP: faster attack rate with melee else PlayAnimPivot('Attack'); } } // ---------------------------------------------------------------------- // PlayTurning() // ---------------------------------------------------------------------- function PlayTurning() { // ClientMessage("PlayTurning()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , , , GetSwimPivot()); else { if (HasTwoHandedWeapon()) TweenAnimPivot('Walk2H', 0.1); else TweenAnimPivot('Walk', 0.1); } } // ---------------------------------------------------------------------- // TweenToWalking() // ---------------------------------------------------------------------- function TweenToWalking(float tweentime) { // ClientMessage("TweenToWalking()"); bIsWalking = True; if (Region.Zone.bWaterZone) TweenAnimPivot('Tread', tweentime, GetSwimPivot()); else { if (HasTwoHandedWeapon()) TweenAnimPivot('Walk2H', tweentime); else TweenAnimPivot('Walk', tweentime); } } // ---------------------------------------------------------------------- // PlayWalking() // ---------------------------------------------------------------------- function PlayWalking() { // ClientMessage("PlayWalking()"); bIsWalking = True; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.15, , GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('Walk2H',walkAnimMult, 0.15); else LoopAnimPivot('Walk',walkAnimMult, 0.15); } } // ---------------------------------------------------------------------- // TweenToRunning() // ---------------------------------------------------------------------- function TweenToRunning(float tweentime) { // ClientMessage("TweenToRunning()"); bIsWalking = False; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',, tweentime,, GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('RunShoot2H', runAnimMult, tweentime); else LoopAnimPivot('Run', runAnimMult, tweentime); } } // ---------------------------------------------------------------------- // PlayRunning() // ---------------------------------------------------------------------- function PlayRunning() { // ClientMessage("PlayRunning()"); bIsWalking = False; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('RunShoot2H', runAnimMult); else LoopAnimPivot('Run', runAnimMult); } } // ---------------------------------------------------------------------- // PlayPanicRunning() // ---------------------------------------------------------------------- function PlayPanicRunning() { // ClientMessage("PlayPanicRunning()"); bIsWalking = False; if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else LoopAnimPivot('Panic', runAnimMult); } // ---------------------------------------------------------------------- // TweenToWaiting() // ---------------------------------------------------------------------- function TweenToWaiting(float tweentime) { // ClientMessage("TweenToWaiting()"); if (Region.Zone.bWaterZone) TweenAnimPivot('Tread', tweentime, GetSwimPivot()); else { if (HasTwoHandedWeapon()) TweenAnimPivot('BreatheLight2H', tweentime); else TweenAnimPivot('BreatheLight', tweentime); } } // ---------------------------------------------------------------------- // PlayWaiting() // ---------------------------------------------------------------------- function PlayWaiting() { // ClientMessage("PlayWaiting()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.3, , GetSwimPivot()); else { if (HasTwoHandedWeapon()) LoopAnimPivot('BreatheLight2H', , 0.3); else LoopAnimPivot('BreatheLight', , 0.3); } } // ---------------------------------------------------------------------- // PlayIdle() // ---------------------------------------------------------------------- function PlayIdle() { // ClientMessage("PlayIdle()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.3, , GetSwimPivot()); else { if (HasTwoHandedWeapon()) PlayAnimPivot('Idle12H', , 0.3); else PlayAnimPivot('Idle1', , 0.3); } } // ---------------------------------------------------------------------- // PlayDancing() // ---------------------------------------------------------------------- function PlayDancing() { // ClientMessage("PlayDancing()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread', , 0.3, , GetSwimPivot()); else LoopAnimPivot('Dance', FRand()*0.2+0.9, 0.3); } // ---------------------------------------------------------------------- // PlaySittingDown() // ---------------------------------------------------------------------- function PlaySittingDown() { // ClientMessage("PlaySittingDown()"); PlayAnimPivot('SitBegin', , 0.15); } // ---------------------------------------------------------------------- // PlaySitting() // ---------------------------------------------------------------------- function PlaySitting() { // ClientMessage("PlaySitting()"); LoopAnimPivot('SitBreathe', , 0.15); } // ---------------------------------------------------------------------- // PlayStandingUp() // ---------------------------------------------------------------------- function PlayStandingUp() { // ClientMessage("PlayStandingUp()"); PlayAnimPivot('SitStand', , 0.15); } // ---------------------------------------------------------------------- // PlayRubbingEyesStart() // ---------------------------------------------------------------------- function PlayRubbingEyesStart() { // ClientMessage("PlayRubbingEyesStart()"); PlayAnimPivot('RubEyesStart', , 0.15); } // ---------------------------------------------------------------------- // PlayRubbingEyes() // ---------------------------------------------------------------------- function PlayRubbingEyes() { // ClientMessage("PlayRubbingEyes()"); LoopAnimPivot('RubEyes'); } // ---------------------------------------------------------------------- // PlayRubbingEyesEnd() // ---------------------------------------------------------------------- function PlayRubbingEyesEnd() { // ClientMessage("PlayRubbingEyesEnd()"); PlayAnimPivot('RubEyesStop'); } // ---------------------------------------------------------------------- // PlayCowerBegin() // ---------------------------------------------------------------------- function PlayCowerBegin() { // ClientMessage("PlayCowerBegin()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else PlayAnimPivot('CowerBegin'); } // ---------------------------------------------------------------------- // PlayCowering() // ---------------------------------------------------------------------- function PlayCowering() { // ClientMessage("PlayCowering()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else LoopAnimPivot('CowerStill'); } // ---------------------------------------------------------------------- // PlayCowerEnd() // ---------------------------------------------------------------------- function PlayCowerEnd() { // ClientMessage("PlayCowerEnd()"); if (Region.Zone.bWaterZone) LoopAnimPivot('Tread',,,,GetSwimPivot()); else PlayAnimPivot('CowerEnd'); } // ---------------------------------------------------------------------- // PlayStunned() // ---------------------------------------------------------------------- function PlayStunned() { // ClientMessage("PlayStunned()"); LoopAnimPivot('Shocked'); } // ---------------------------------------------------------------------- // TweenToSwimming() // ---------------------------------------------------------------------- function TweenToSwimming(float tweentime) { // ClientMessage("TweenToSwimming()"); TweenAnimPivot('Tread', tweentime, GetSwimPivot()); } // ---------------------------------------------------------------------- // PlaySwimming() // ---------------------------------------------------------------------- function PlaySwimming() { // ClientMessage("PlaySwimming()"); LoopAnimPivot('Tread', , , , GetSwimPivot()); } // ---------------------------------------------------------------------- // PlayFalling() // ---------------------------------------------------------------------- function PlayFalling() { // ClientMessage("PlayFalling()"); PlayAnimPivot('Jump', 3, 0.1); } // ---------------------------------------------------------------------- // PlayLanded() // ---------------------------------------------------------------------- function PlayLanded(float impactVel) { // ClientMessage("PlayLanded()"); bIsWalking = True; if (impactVel < -12*CollisionHeight) PlayAnimPivot('Land'); } // ---------------------------------------------------------------------- // PlayDuck() // ---------------------------------------------------------------------- function PlayDuck() { // ClientMessage("PlayDuck()"); TweenAnimPivot('CrouchWalk', 0.25); // PlayAnimPivot('Crouch'); } // ---------------------------------------------------------------------- // PlayRising() // ---------------------------------------------------------------------- function PlayRising() { // ClientMessage("PlayRising()"); PlayAnimPivot('Stand'); } // ---------------------------------------------------------------------- // PlayCrawling() // ---------------------------------------------------------------------- function PlayCrawling() { // ClientMessage("PlayCrawling()"); LoopAnimPivot('CrouchWalk'); } // ---------------------------------------------------------------------- // PlayPushing() // ---------------------------------------------------------------------- function PlayPushing() { // ClientMessage("PlayPushing()"); PlayAnimPivot('PushButton',1.2 , 0.15); //CyberP: added 1.2 rate } // ---------------------------------------------------------------------- // PlayBeginAttack() // ---------------------------------------------------------------------- function bool PlayBeginAttack() { return false; } // ---------------------------------------------------------------------- // PlayFiring() // ---------------------------------------------------------------------- /* function PlayFiring() { local DeusExWeapon W; // ClientMessage("PlayFiring()"); W = DeusExWeapon(Weapon); if (W != None) { if (W.bHandToHand) { PlayAnimPivot('Attack',1.1,0.1); } else { if (W.bAutomatic) { if (HasTwoHandedWeapon()) LoopAnimPivot('Shoot2H',,0.1); else LoopAnimPivot('Shoot',,0.1); } else { if (HasTwoHandedWeapon()) PlayAnimPivot('Shoot2H',,0.1); else PlayAnimPivot('Shoot',,0.1); } } } } */ // ---------------------------------------------------------------------- // PlayTakingHit() // ---------------------------------------------------------------------- function PlayTakingHit(EHitLocation hitPos) { local vector pivot; local name animName; animName = ''; if (!Region.Zone.bWaterZone) { switch (hitPos) { case HITLOC_HeadFront: animName = 'HitHead'; break; case HITLOC_TorsoFront: animName = 'HitTorso'; break; case HITLOC_LeftArmFront: animName = 'HitArmLeft'; break; case HITLOC_RightArmFront: animName = 'HitArmRight'; break; case HITLOC_HeadBack: animName = 'HitHeadBack'; break; case HITLOC_TorsoBack: case HITLOC_LeftArmBack: case HITLOC_RightArmBack: animName = 'HitTorsoBack'; break; case HITLOC_LeftLegFront: case HITLOC_LeftLegBack: animName = 'HitLegLeft'; break; case HITLOC_RightLegFront: case HITLOC_RightLegBack: animName = 'HitLegRight'; break; } pivot = vect(0,0,0); } else { switch (hitPos) { case HITLOC_HeadFront: case HITLOC_TorsoFront: case HITLOC_LeftLegFront: case HITLOC_RightLegFront: case HITLOC_LeftArmFront: case HITLOC_RightArmFront: animName = 'WaterHitTorso'; break; case HITLOC_HeadBack: case HITLOC_TorsoBack: case HITLOC_LeftLegBack: case HITLOC_RightLegBack: case HITLOC_LeftArmBack: case HITLOC_RightArmBack: animName = 'WaterHitTorsoBack'; break; } pivot = GetSwimPivot(); } if (animName != '') PlayAnimPivot(animName,0.9 , 0.1, pivot); //CyberP: 0.9 added } // ---------------------------------------------------------------------- // PlayWeaponSwitch() // ---------------------------------------------------------------------- function PlayWeaponSwitch(Weapon newWeapon) { // ClientMessage("PlayWeaponSwitch()"); } // ---------------------------------------------------------------------- // PlayDying() // ---------------------------------------------------------------------- function PlayDying(name damageType, vector hitLoc) { local Vector X, Y, Z; local float dotp; local float multa; // ClientMessage("PlayDying()"); if (bFlyer && Physics == PHYS_Flying) multa= 1.45; else multa= 1; if (Region.Zone.bWaterZone) PlayAnimPivot('WaterDeath',, 0.1); else if (bSitting) // if sitting, always fall forward PlayAnimPivot('DeathFront',1.1, 0.1); //CyberP: else if (AnimSequence == 'PushButton' ) PlayAnimPivot('DeathBack',1.05*multa, 0.1); else { GetAxes(Rotation, X, Y, Z); dotp = (Location - HitLoc) dot X; // die from the correct side if (dotp < 0.0) // shot from the front, fall back PlayAnimPivot('DeathBack',1.05*multa, 0.1); //cyberP: sped up death back else // shot from the back, fall front PlayAnimPivot('DeathFront',1.05*multa, 0.1); //CyberP: sped up death front } // don't scream if we are stunned if ((damageType == 'Stunned') || (damageType == 'KnockedOut') || (damageType == 'Poison') || (damageType == 'PoisonEffect')) { bStunned = True; if (bIsFemale) //CyberP: lets add a bit of variety to K.O sounds. { if (FRand() < 0.5) PlaySound(Sound'FemaleUnconscious', SLOT_Pain,,,, RandomPitch()); else { } } else if (IsA('MJ12Commando')) PlaySound(Sound'CloakDown', SLOT_Pain,,,, RandomPitch()); else if ((IsA('HKMilitary') || IsA('TriadLumPath')) && (FRand() < 0.7)) PlaySound(Sound'Death07', SLOT_Pain); else if (FRand() < 0.25) PlaySound(Sound'Death09', SLOT_Pain); else if (FRand() < 0.4) { } else PlaySound(Sound'MaleUnconscious', SLOT_Pain,,,, RandomPitch()); } else { bStunned = False; PlayDyingSound(); } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // FIRE ROUTINES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CatchFire() // ---------------------------------------------------------------------- //ok, this is going to be completely replaced with a system devised by smoke39, who also //discovered the renderoverlays trick. Which is nice. -DDL function CatchFire() { local Fire f; local int i; local vector loc; if (bOnFire || Region.Zone.bWaterZone || (BurnPeriod <= 0) || bInvincible) return; bOnFire = True; burnTimer = 0; EnableCloak(false); // for (i=0; i<8; i++) // { // loc.X = 0.5*CollisionRadius * (1.0-2.0*FRand()); // loc.Y = 0.5*CollisionRadius * (1.0-2.0*FRand()); // loc.Z = 0.6*CollisionHeight * (1.0-2.0*FRand()); // loc += Location; // f = Spawn(class'Fire', Self,, loc); // if (f != None) // { // f.DrawScale = 0.5*FRand() + 1.0; // // // turn off the sound and lights for all but the first one // if (i > 0) // { // f.AmbientSound = None; // f.LightType = LT_None; // } // // // turn on/off extra fire and smoke // if (FRand() < 0.5) // f.smokeGen.Destroy(); // if (FRand() < 0.5) // f.AddFire(); // } // } //smoke39's system Spawn(class'FireMesh', self); // set the burn timer SetTimer(1.0, True); } // ---------------------------------------------------------------------- // ExtinguishFire() // ---------------------------------------------------------------------- function ExtinguishFire() { local FireMesh f; bOnFire = False; burnTimer = 0; SetTimer(0, False); // foreach BasedActors(class'Fire', f) // f.Destroy(); foreach BasedActors(class'FireMesh', f) { // Smoke39 - don't destroy it, just make it smoke // f.Destroy(); if(Health <= 0) f.bDying = true; f.Smoke(); } } // ---------------------------------------------------------------------- // UpdateFire() // ---------------------------------------------------------------------- function UpdateFire() { // continually burn and do damage HealthTorso -= 10; if (IsA('MIB')) HealthTorso -= 20; GenerateTotalHealth(); if (Health <= 0) { TakeDamage(20, None, Location, vect(0,0,0), 'Burned'); ExtinguishFire(); } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CONVERSATION FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CanConverse() // ---------------------------------------------------------------------- function bool CanConverse() { // Return True if this NPC is in a conversable state return (bCanConverse && bInterruptState && ((Physics == PHYS_Walking) || (Physics == PHYS_Flying))); } // ---------------------------------------------------------------------- // CanConverseWithPlayer() // ---------------------------------------------------------------------- function bool CanConverseWithPlayer(DeusExPlayer dxPlayer) { local name alliance1, alliance2, carcname; // temp vars if (GetPawnAllianceType(dxPlayer) == ALLIANCE_Hostile) return false; else if ((GetStateName() == 'Fleeing') && (Enemy != dxPlayer) && (IsValidEnemy(Enemy, false))) // hack return false; else if (GetCarcassData(dxPlayer, alliance1, alliance2, carcname)) return false; else return true; } // ---------------------------------------------------------------------- // EndConversation() // ---------------------------------------------------------------------- function EndConversation() { Super.EndConversation(); if ((GetStateName() == 'Conversation') || (GetStateName() == 'FirstPersonConversation')) { bConversationEndedNormally = True; if (!bConvEndState) FollowOrders(); } bInConversation = False; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // STIMULI AND AGITATION ROUTINES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // LoudNoiseScore() // ---------------------------------------------------------------------- function float LoudNoiseScore(actor receiver, actor sender, float score) { local Pawn pawnSender; // Cull events received from friends pawnSender = Pawn(sender); if (pawnSender == None) pawnSender = sender.Instigator; if (pawnSender == None) score = 0; else if (!IsValidEnemy(pawnSender)) score = 0; return score; } // ---------------------------------------------------------------------- // WeaponDrawnScore() // ---------------------------------------------------------------------- function float WeaponDrawnScore(actor receiver, actor sender, float score) { local Pawn pawnSender; // Cull events received from enemies pawnSender = Pawn(sender); if (pawnSender == None) pawnSender = Pawn(sender.Owner); if (pawnSender == None) pawnSender = sender.Instigator; if (pawnSender == None) score = 0; else if (IsValidEnemy(pawnSender)) score = 0; return score; } // ---------------------------------------------------------------------- // DistressScore() // ---------------------------------------------------------------------- function float DistressScore(actor receiver, actor sender, float score) { local ScriptedPawn scriptedSender; local Pawn pawnSender; // Cull events received from enemies sender = InstigatorToPawn(sender); // hack pawnSender = Pawn(sender); scriptedSender = ScriptedPawn(sender); if (pawnSender == None) score = 0; else if ((GetPawnAllianceType(pawnSender) != ALLIANCE_Friendly) && !bFearDistress) score = 0; else if ((scriptedSender != None) && !scriptedSender.bDistressed) score = 0; return score; } // ---------------------------------------------------------------------- // UpdateReactionCallbacks() // ---------------------------------------------------------------------- function UpdateReactionCallbacks() { if (bReactFutz && bLookingForFutz) AISetEventCallback('Futz', 'HandleFutz', , true, true, false, true); else AIClearEventCallback('Futz'); if ((bHateHacking || bFearHacking) && bLookingForHacking) AISetEventCallback('MegaFutz', 'HandleHacking', , true, true, true, true); else AIClearEventCallback('MegaFutz'); if ((bHateWeapon || bFearWeapon) && bLookingForWeapon) AISetEventCallback('WeaponDrawn', 'HandleWeapon', 'WeaponDrawnScore', true, true, false, true); else AIClearEventCallback('WeaponDrawn'); if ((bReactShot || bFearShot || bHateShot) && bLookingForShot) AISetEventCallback('WeaponFire', 'HandleShot', , true, true, false, true); else AIClearEventCallback('WeaponFire'); /* if ((bHateCarcass || bReactCarcass || bFearCarcass) && bLookingForCarcass) AISetEventCallback('Carcass', 'HandleCarcass', 'CarcassScore', true, true, false, true); else AIClearEventCallback('Carcass'); */ if (bReactLoudNoise && bLookingForLoudNoise) { AISetEventCallback('LoudNoise', 'HandleLoudNoise', 'LoudNoiseScore'); //GMDX: more generic noise, like whats that a rock! AISetEventCallback('GenericNoise', 'HandleGenericNoise'); } else { AIClearEventCallback('LoudNoise'); AIClearEventCallback('GenericNoise'); } if ((bReactAlarm || bFearAlarm) && bLookingForAlarm) AISetEventCallback('Alarm', 'HandleAlarm'); else AIClearEventCallback('Alarm'); if ((bHateDistress || bReactDistress || bFearDistress) && bLookingForDistress) AISetEventCallback('Distress', 'HandleDistress', 'DistressScore', true, true, false, true); else AIClearEventCallback('Distress'); if ((bFearProjectiles || bReactProjectiles) && bLookingForProjectiles) AISetEventCallback('Projectile', 'HandleProjectiles', , false, true, false, true); else AIClearEventCallback('Projectile'); } // ---------------------------------------------------------------------- // SetReactions() // ---------------------------------------------------------------------- function SetReactions(bool bEnemy, bool bLoudNoise, bool bAlarm, bool bDistress, bool bProjectile, bool bFutz, bool bHacking, bool bShot, bool bWeapon, bool bCarcass, bool bInjury, bool bIndirectInjury) { bLookingForEnemy = bEnemy; bLookingForLoudNoise = bLoudNoise; bLookingForAlarm = bAlarm; bLookingForDistress = bDistress; bLookingForProjectiles = bProjectile; bLookingForFutz = bFutz; bLookingForHacking = bHacking; bLookingForShot = bShot; bLookingForWeapon = bWeapon; bLookingForCarcass = bCarcass; bLookingForInjury = bInjury; bLookingForIndirectInjury = bIndirectInjury; UpdateReactionCallbacks(); } // ---------------------------------------------------------------------- // BlockReactions() // ---------------------------------------------------------------------- function BlockReactions(optional bool bBlockInjury) { SetReactions(false, false, false, false, false, false, false, false, false, false, !bBlockInjury, !bBlockInjury); } // ---------------------------------------------------------------------- // ResetReactions() // ---------------------------------------------------------------------- function ResetReactions() { SetReactions(true, true, true, true, true, true, true, true, true, true, true, true); } // ---------------------------------------------------------------------- // HandleFutz() // ---------------------------------------------------------------------- function HandleFutz(Name event, EAIEventState state, XAIParams params) { local Pawn pawnActor; local DeusExPlayer dxPlayer; local AdaptiveArmor armor; if (bLookingForFutz && bReactFutz) { if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = GetPlayerPawn(); dxPlayer = DeusExPlayer(GetPlayerPawn()); if (pawnActor != None && dxPlayer != none && dxPlayer.AugmentationSystem.GetAugLevelValue(class'AugCloak') <= -1.0) { foreach AllActors(class'AdaptiveArmor', armor) //CyberP: thermoptic camo hides us from detection if ((armor.Owner == dxPlayer) && armor.bActive) return; ReactToFutz(); // only players can futz if (dxplayer.bHardcoreAI3 && FRand() < 0.4) { if (!IsA('ThugMale') && !IsA('NicoletteDuClare') && (IsA('HumanThug') || IsA('HumanMilitary'))) { IncreaseAgitation(pawnActor, 1.0); if (SetEnemy(pawnActor)) { SetDistressTimer(); HandleEnemy(); } } } } } } } // ---------------------------------------------------------------------- // HandleHacking() // ---------------------------------------------------------------------- function HandleHacking(Name event, EAIEventState state, XAIParams params) { // Fear, Hate local Pawn pawnActor; local DeusExPlayer dxPlayer; local AdaptiveArmor armor; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = GetPlayerPawn(); dxPlayer = DeusExPlayer(GetPlayerPawn()); if (pawnActor != None && dxPlayer != none && dxPlayer.AugmentationSystem.GetAugLevelValue(class'AugCloak') <= -1.0) { foreach AllActors(class'AdaptiveArmor', armor) //CyberP: thermoptic camo hides us if ((armor.Owner == dxPlayer) && armor.bActive) return; if (bHateHacking) IncreaseAgitation(pawnActor, 1.0); if (bFearHacking) IncreaseFear(pawnActor, 0.51); if (SetEnemy(pawnActor)) { SetDistressTimer(); HandleEnemy(); } else if (bFearHacking && IsFearful()) { SetDistressTimer(); SetEnemy(pawnActor, , true); GotoState('Fleeing'); } else // only players can hack ReactToFutz(); } } } // ---------------------------------------------------------------------- // HandleWeapon() // ---------------------------------------------------------------------- function HandleWeapon(Name event, EAIEventState state, XAIParams params) { // Fear, Hate local Pawn pawnActor; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = InstigatorToPawn(params.bestActor); if (pawnActor != None) { if (bHateWeapon) IncreaseAgitation(pawnActor); if (bFearWeapon) IncreaseFear(pawnActor, 1.0); // Let presence checking handle enemy sighting if (!IsValidEnemy(pawnActor)) { if (bFearWeapon && IsFearful()) { SetDistressTimer(); SetEnemy(pawnActor, , true); GotoState('Fleeing'); } else if (pawnActor.bIsPlayer) ReactToFutz(); } } } } // ---------------------------------------------------------------------- // HandleShot() // ---------------------------------------------------------------------- function HandleShot(Name event, EAIEventState state, XAIParams params) { // React, Fear, Hate local Pawn pawnActor; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { pawnActor = InstigatorToPawn(params.bestActor); if (pawnActor != None) { if (bHateShot) IncreaseAgitation(pawnActor); if (bFearShot) IncreaseFear(pawnActor, 1.0); if (SetEnemy(pawnActor)) { SetDistressTimer(); HandleEnemy(); } else if (bFearShot && IsFearful()) { SetDistressTimer(); SetEnemy(pawnActor, , true); GotoState('Fleeing'); } else if (pawnActor.bIsPlayer) ReactToFutz(); } } } // ---------------------------------------------------------------------- // HandleLoudNoise() // ---------------------------------------------------------------------- function HandleLoudNoise(Name event, EAIEventState state, XAIParams params) { // React local Actor bestActor; local Pawn instigator; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if (bestActor != None) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { if (IsValidEnemy(instigator)) { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); HandleEnemy(); } } } } } // ---------------------------------------------------------------------- // GMDX: HandleGenericNoise() // ---------------------------------------------------------------------- function HandleGenericNoise(Name event, EAIEventState state, XAIParams params) { // React local Actor bestActor; local Pawn instigator; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if (bestActor != None) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { // if (IsValidEnemy(instigator)) // { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); if (Enemy == None) GotoState('Seeking','GoToLocation'); else if (RaiseAlarm == RAISEALARM_BeforeAttacking) GotoState('Alerting'); // else // GotoState('Attacking'); // } } } } } // ---------------------------------------------------------------------- // HandleProjectiles() // ---------------------------------------------------------------------- function HandleProjectiles(Name event, EAIEventState state, XAIParams params) { // React, Fear local DeusExProjectile dxProjectile; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) if (params.bestActor != None) ReactToProjectiles(params.bestActor); } // ---------------------------------------------------------------------- // HandleAlarm() // ---------------------------------------------------------------------- function HandleAlarm(Name event, EAIEventState state, XAIParams params) { // React, Fear local AlarmUnit alarm; local LaserTrigger laser; local SecurityCamera camera; local Computers computer; local Pawn alarmInstigator; local vector alarmLocation; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { alarmInstigator = None; alarm = AlarmUnit(params.bestActor); laser = LaserTrigger(params.bestActor); camera = SecurityCamera(params.bestActor); computer = Computers(params.bestActor); if (alarm != None) { alarmInstigator = alarm.alarmInstigator; alarmLocation = alarm.alarmLocation; } else if (laser != None) { alarmInstigator = Pawn(laser.triggerActor); if (alarmInstigator == None) alarmInstigator = laser.triggerActor.Instigator; alarmLocation = laser.actorLocation; } else if (camera != None) { alarmInstigator = GetPlayerPawn(); // player is implicit for cameras alarmLocation = camera.playerLocation; } else if (computer != None) { alarmInstigator = GetPlayerPawn(); // player is implicit for computers alarmLocation = computer.Location; } if (bFearAlarm) { IncreaseFear(alarmInstigator, 2.0); if (IsFearful()) { SetDistressTimer(); SetEnemy(alarmInstigator, , true); GotoState('Fleeing'); } } if (alarmInstigator != None) { if (alarmInstigator.Health > 0) { if (IsValidEnemy(alarmInstigator)) { AlarmTimer = 120; SetDistressTimer(); if (IsA('HumanMilitary') && !bAlarmStatIncrease) //CyberP: AI get better stats if alarm is triggered. { bAlarmStatIncrease=True; if (SurprisePeriod > 0.5) SurprisePeriod -= 0.100000; if (VisibilityThreshold > 0.002000) VisibilityThreshold -= 0.000500; if (BaseAccuracy > 0.010000) BaseAccuracy -= 0.010000; //CyberP: So, the result is improved accuracy, reload speed, cooldown time, } //vision & reaction time. if (FRand() < 0.5) SetSeekLocation(alarmInstigator, alarmLocation, SEEKTYPE_Guess, true); //CyberP: different reaction else SetSeekLocation(alarmInstigator, alarmLocation, SEEKTYPE_Sound); HandleEnemy(); } } } } } // ---------------------------------------------------------------------- // HandleDistress() // ---------------------------------------------------------------------- function HandleDistress(Name event, EAIEventState state, XAIParams params) { // React, Fear, Hate local float seeTime; local Pawn distressee; local DeusExPlayer distresseePlayer; local ScriptedPawn distresseePawn; local Pawn distressor; local DeusExPlayer distressorPlayer; local ScriptedPawn distressorPawn; local bool bDistresseeValid; local bool bDistressorValid; local float distressVal; local name stateName; local bool bAttacking; local bool bFleeing; bAttacking = false; seeTime = 0; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { distressee = InstigatorToPawn(params.bestActor); if (distressee != None) { if (bFearDistress) IncreaseFear(distressee.Enemy, 1.0); bDistresseeValid = false; bDistressorValid = false; distresseePlayer = DeusExPlayer(distressee); distresseePawn = ScriptedPawn(distressee); if (GetPawnAllianceType(distressee) == ALLIANCE_Friendly) { if (distresseePawn != None) { if (distresseePawn.bDistressed && (distresseePawn.EnemyLastSeen <= EnemyTimeout)) { bDistresseeValid = true; seeTime = distresseePawn.EnemyLastSeen; } } else if (distresseePlayer != None) bDistresseeValid = true; } if (bDistresseeValid) { distressor = distressee.Enemy; distressorPlayer = DeusExPlayer(distressor); distressorPawn = ScriptedPawn(distressor); if (distressorPawn != None) { if (bHateDistress || (distressorPawn.GetPawnAllianceType(distressee) == ALLIANCE_Hostile)) bDistressorValid = true; } else if (distresseePawn != None) { if (bHateDistress || (distresseePawn.GetPawnAllianceType(distressor) == ALLIANCE_Hostile)) bDistressorValid = true; } // Finally, react if (bDistressorValid) { if (bHateDistress) IncreaseAgitation(distressor, 1.0); if (SetEnemy(distressor, seeTime)) { SetDistressTimer(); HandleEnemy(); bAttacking = true; } } // BOOGER! Make NPCs react by seeking if distressor isn't an enemy? } if (!bAttacking && bFearDistress) { distressVal = 0; bFleeing = false; if (distresseePawn != None) { stateName = distresseePawn.GetStateName(); if (stateName == 'Fleeing') // hack -- to prevent infinite fleeing { if (distresseePawn.DistressTimer >= 0) { if (FearSustainTime - distresseePawn.DistressTimer >= 1) { IncreaseFear(distressee.Enemy, 1.0, distresseePawn.DistressTimer); distressVal = distresseePawn.DistressTimer; bFleeing = true; } } } else { IncreaseFear(distressee.Enemy, 1.0); bFleeing = true; } } else { IncreaseFear(distressee.Enemy, 1.0); bFleeing = true; } if (bFleeing && IsFearful()) { if ((DistressTimer > distressVal) || (DistressTimer < 0)) DistressTimer = distressVal; SetEnemy(distressee.Enemy, , true); GotoState('Fleeing'); } } } } } // ---------------------------------------------------------------------- // IncreaseFear() // ---------------------------------------------------------------------- function IncreaseFear(Actor actorInstigator, float addedFearLevel, optional float newFearTimer) { local DeusExPlayer player; local Pawn instigator; instigator = InstigatorToPawn(actorInstigator); if (instigator != None) { if (FearTimer < (FearSustainTime-newFearTimer)) FearTimer = FearSustainTime-newFearTimer; if (FearTimer > 0) { if (addedFearLevel > 0) { FearLevel += addedFearLevel; if (FearLevel > 1.0) FearLevel = 1.0; } } } } // ---------------------------------------------------------------------- // IncreaseAgitation() // ---------------------------------------------------------------------- function IncreaseAgitation(Actor actorInstigator, optional float AgitationLevel) { local Pawn instigator; local float minLevel; instigator = InstigatorToPawn(actorInstigator); if (instigator != None) { AgitationTimer = AgitationSustainTime; if (AgitationCheckTimer <= 0) { AgitationCheckTimer = 1.5; // hardcoded for now if (AgitationLevel == 0) { if (MaxProvocations < 0) MaxProvocations = 0; AgitationLevel = 1.0/(MaxProvocations+1); } if (AgitationLevel > 0) { bAlliancesChanged = True; bNoNegativeAlliances = False; AgitateAlliance(instigator.Alliance, AgitationLevel); } } } } // ---------------------------------------------------------------------- // DecreaseAgitation() // ---------------------------------------------------------------------- function DecreaseAgitation(Actor actorInstigator, float AgitationLevel) { local float newLevel; local DeusExPlayer player; local Pawn instigator; player = DeusExPlayer(GetPlayerPawn()); if (Inventory(actorInstigator) != None) { if (Inventory(actorInstigator).Owner != None) actorInstigator = Inventory(actorInstigator).Owner; } else if (DeusExDecoration(actorInstigator) != None) actorInstigator = player; instigator = Pawn(actorInstigator); if ((instigator == None) || (instigator == self)) return; AgitationTimer = AgitationSustainTime; if (AgitationLevel > 0) { bAlliancesChanged = True; bNoNegativeAlliances = False; AgitateAlliance(instigator.Alliance, -AgitationLevel); } } // ---------------------------------------------------------------------- // UpdateAgitation() // ---------------------------------------------------------------------- function UpdateAgitation(float deltaSeconds) { local float mult; local float decrement; local int i; if (AgitationCheckTimer > 0) { AgitationCheckTimer -= deltaSeconds; if (AgitationCheckTimer < 0) AgitationCheckTimer = 0; } decrement = 0; if (AgitationTimer > 0) { if (AgitationTimer < deltaSeconds) { mult = 1.0 - (AgitationTimer/deltaSeconds); AgitationTimer = 0; decrement = mult * (AgitationDecayRate*deltaSeconds); } else AgitationTimer -= deltaSeconds; } else decrement = AgitationDecayRate*deltaSeconds; if (bAlliancesChanged && (decrement > 0)) { bAlliancesChanged = False; for (i=15; i>=0; i--) { if ((AlliancesEx[i].AllianceName != '') && (!AlliancesEx[i].bPermanent)) { if (AlliancesEx[i].AgitationLevel > 0) { bAlliancesChanged = true; AlliancesEx[i].AgitationLevel -= decrement; if (AlliancesEx[i].AgitationLevel < 0) AlliancesEx[i].AgitationLevel = 0; } } } } } // ---------------------------------------------------------------------- // UpdateFear() // ---------------------------------------------------------------------- function UpdateFear(float deltaSeconds) { local float mult; local float decrement; local int i; decrement = 0; if (FearTimer > 0) { if (FearTimer < deltaSeconds) { mult = 1.0 - (FearTimer/deltaSeconds); FearTimer = 0; decrement = mult * (FearDecayRate*deltaSeconds); } else FearTimer -= deltaSeconds; } else decrement = FearDecayRate*deltaSeconds; if ((decrement > 0) && (FearLevel > 0)) { FearLevel -= decrement; if (FearLevel < 0) FearLevel = 0; } } // ---------------------------------------------------------------------- // IsFearful() // ---------------------------------------------------------------------- function bool IsFearful() { if (FearLevel >= 1.0) return true; else return false; } // ---------------------------------------------------------------------- // ShouldBeStartled() [stub function, overridden by subclasses] // ---------------------------------------------------------------------- function bool ShouldBeStartled(Pawn startler) { return false; } // ---------------------------------------------------------------------- // ShouldPlayTurn() // ---------------------------------------------------------------------- function bool ShouldPlayTurn(vector lookdir) { local Rotator rot; rot = Rotator(lookdir); rot.Yaw = (rot.Yaw - Rotation.Yaw) & 65535; if (rot.Yaw > 32767) rot.Yaw = 65536 - rot.Yaw; // negate if (rot.Yaw > 4096) return true; else return false; } // ---------------------------------------------------------------------- // ShouldPlayWalk() // ---------------------------------------------------------------------- function bool ShouldPlayWalk(vector movedir) { local vector diff; if (Physics == PHYS_Falling) return true; else if (Physics == PHYS_Walking) { diff = (movedir - Location) * vect(1,1,0); if (VSize(diff) < 16) return false; else return true; } else if (VSize(movedir-Location) < 16) return false; else return true; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // ALLIANCE ROUTINES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // SetAlliance() // ---------------------------------------------------------------------- function SetAlliance(Name newAlliance) { Alliance = newAlliance; } // ---------------------------------------------------------------------- // ChangeAlly() // ---------------------------------------------------------------------- function ChangeAlly(Name newAlly, optional float allyLevel, optional bool bPermanent, optional bool bHonorPermanence) { local int i; // Members of the same alliance will ALWAYS be friendly to each other if (newAlly == Alliance) { allyLevel = 1; bPermanent = true; } if (bHonorPermanence) { for (i=0; i<16; i++) if (AlliancesEx[i].AllianceName == newAlly) if (AlliancesEx[i].bPermanent) break; if (i < 16) return; } if (allyLevel < -1) allyLevel = -1; if (allyLevel > 1) allyLevel = 1; for (i=0; i<16; i++) if ((AlliancesEx[i].AllianceName == newAlly) || (AlliancesEx[i].AllianceName == '')) break; if (i >= 16) for (i=15; i>0; i--) AlliancesEx[i] = AlliancesEx[i-1]; AlliancesEx[i].AllianceName = newAlly; AlliancesEx[i].AllianceLevel = allyLevel; AlliancesEx[i].AgitationLevel = 0; AlliancesEx[i].bPermanent = bPermanent; bAlliancesChanged = True; bNoNegativeAlliances = False; } // ---------------------------------------------------------------------- // AgitateAlliance() // ---------------------------------------------------------------------- function AgitateAlliance(Name newEnemy, float agitation) { local int i; local float oldLevel; local float newLevel; if (newEnemy != '') { for (i=0; i<16; i++) if ((AlliancesEx[i].AllianceName == newEnemy) || (AlliancesEx[i].AllianceName == '')) break; if (i < 16) { if ((AlliancesEx[i].AllianceName == '') || !(AlliancesEx[i].bPermanent)) { if (AlliancesEx[i].AllianceName == '') AlliancesEx[i].AllianceLevel = 0; oldLevel = AlliancesEx[i].AgitationLevel; newLevel = oldLevel + agitation; if (newLevel > 1.0) newLevel = 1.0; AlliancesEx[i].AllianceName = newEnemy; AlliancesEx[i].AgitationLevel = newLevel; if ((newEnemy == 'Player') && (oldLevel < 1.0) && (newLevel >= 1.0)) // hack PlayerAgitationTimer = 2.0; bAlliancesChanged = True; } } } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // ATTACKING FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // AISafeToShoot() // ---------------------------------------------------------------------- function bool AISafeToShoot(out Actor hitActor, vector traceEnd, vector traceStart, optional vector extent, optional bool bIgnoreLevel) { local Actor traceActor; local Vector hitLocation; local Vector hitNormal; local Pawn tracePawn; local DeusExDecoration traceDecoration; local DeusExMover traceMover; local bool bSafe; //local float dista; // Future improvement: // Ideally, this should use the ammo type to determine how many shots // it will take to destroy an obstruction, and call it unsafe if it takes // more than x shots. Also, if the ammo is explosive, and the // obstruction is too close, it should never be safe... bSafe = true; hitActor = None; foreach TraceActors(Class'Actor', traceActor, hitLocation, hitNormal, traceEnd, traceStart, extent) { if (hitActor == None) hitActor = traceActor; if (traceActor == Level) { //dista = Abs(VSize(Enemy.Location - traceActor.Location)); //CyberP: supressive fire //if (dista < 512) // break; if (!bIgnoreLevel) bSafe = false; break; } tracePawn = Pawn(traceActor); if (tracePawn != None) { if (tracePawn != self) { if (GetPawnAllianceType(tracePawn) == ALLIANCE_Friendly && !tracePawn.IsInState('Dying')) //CyberP: dying pawns are ignored. bSafe = false; break; } } traceDecoration = DeusExDecoration(traceActor); if (traceDecoration != None) { if (traceDecoration.bExplosive || traceDecoration.bInvincible) { bSafe = false; break; } //if ((traceDecoration.HitPoints > 100) || (traceDecoration.minDamageThreshold > 20)) // hack //CyberP: hp was 20, dt was 4 //CyberP: actually, comment out //{ // bSafe = false; // break; //} } traceMover = DeusExMover(traceActor); if (traceMover != None) { if (!traceMover.bBreakable) { bSafe = false; break; } else if ((traceMover.minDamageThreshold > 3)) // hack //CyberP: get them to shoot through breakable glass { bSafe = false; break; } else // hack break; } //if (Inventory(traceActor) != None) //CyberP: cut this out since inventory is breakable or moves when shot //{ // bSafe = false; // break; //} } return (bSafe); } // ---------------------------------------------------------------------- // ComputeThrowAngles() // ---------------------------------------------------------------------- function bool ComputeThrowAngles(vector traceEnd, vector traceStart, float speed, out Rotator angle1, out Rotator angle2) { local float deltaX, deltaY; local float x, y; local float tanAngle1, tanAngle2; local float A, B, C; local float m, n; local float sqrtTerm; local float gravity; local float traceYaw; local bool bValid; bValid = false; // Reduce our problem to two dimensions deltaX = traceEnd.X - traceStart.X; deltaY = traceEnd.Y - traceStart.Y; x = sqrt(deltaX*deltaX + deltaY*deltaY); y = traceEnd.Z - traceStart.Z; gravity = -Region.Zone.ZoneGravity.Z; if ((x > 0) && (gravity > 0)) { A = -gravity*x*x; B = 2*speed*speed*x; C = -gravity*x*x - 2*y*speed*speed; sqrtTerm = B*B - 4*A*C; if (sqrtTerm >= 0) { m = -B/(2*A); n = sqrt(sqrtTerm)/(2*A); tanAngle1 = atan(m+n); tanAngle2 = atan(m-n); angle1 = Rotator(traceEnd - traceStart); angle2 = angle1; angle1.Pitch = tanAngle1*32768/Pi; angle2.Pitch = tanAngle2*32768/Pi; bValid = true; } } return bValid; } // ---------------------------------------------------------------------- // AISafeToThrow() // ---------------------------------------------------------------------- function bool AISafeToThrow(vector traceEnd, vector traceStart, float throwAccuracy, optional vector extent) { local float time1, time2, tempTime; local vector pos1, pos2, tempPos; local rotator rot1, rot2, tempRot; local rotator bestAngle; local bool bSafe; local DeusExWeapon dxWeapon; local Class<ThrownProjectile> throwClass; // Someday, we should check for nearby friendlies within the blast radius // before throwing... // Sanity checks throwClass = None; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) throwClass = Class<ThrownProjectile>(dxWeapon.ProjectileClass); if (throwClass == None) return false; if (extent == vect(0,0,0)) { extent = vect(1,1,0) * throwClass.Default.CollisionRadius; extent.Z = throwClass.Default.CollisionHeight; } if (throwAccuracy < 0.01) throwAccuracy = 0.01; bSafe = false; if (ComputeThrowAngles(traceEnd, traceStart, dxWeapon.ProjectileSpeed, rot1, rot2)) { time1 = ParabolicTrace(pos1, Vector(rot1)*dxWeapon.ProjectileSpeed, traceStart, true, extent, 5.0, throwClass.Default.Elasticity, throwClass.Default.bBounce, 60, throwAccuracy); time2 = ParabolicTrace(pos2, Vector(rot2)*dxWeapon.ProjectileSpeed, traceStart, true, extent, 5.0, throwClass.Default.Elasticity, throwClass.Default.bBounce, 60, throwAccuracy); if ((time1 > 0) || (time2 > 0)) { if ((time1 > time2) && (time2 > 0)) { tempTime = time1; time1 = time2; time2 = tempTime; tempPos = pos1; pos1 = pos2; pos2 = tempPos; tempRot = rot1; rot1 = rot2; rot2 = tempRot; } if (VSize(pos1-traceEnd) <= throwClass.Default.blastRadius) { if (FastTrace(traceEnd, pos1)) { if ((VSize(pos1-Location) > throwClass.Default.blastRadius*0.5) || !FastTrace(Location, pos1)) { bestAngle = rot1; bSafe = true; } } } } if (!bSafe && (time2 > 0)) { if (VSize(pos2-traceEnd) <= throwClass.Default.blastRadius) { if (FastTrace(traceEnd, pos2)) { if ((VSize(pos2-Location) > throwClass.Default.blastRadius*0.5) || !FastTrace(Location, pos2)) { bestAngle = rot2; bSafe = true; } } } } } if (bSafe) ViewRotation = bestAngle; return (bSafe); } // ---------------------------------------------------------------------- // AICanShoot() // ---------------------------------------------------------------------- function bool AICanShoot(pawn target, bool bLeadTarget, bool bCheckReadiness, optional float throwAccuracy, optional bool bDiscountMinRange) { local DeusExWeapon dxWeapon; local Vector X, Y, Z; local Vector projStart, projEnd; local float tempMinRange, tempMaxRange; local float temp; local float dist; local float extraDist; local actor hitActor; local Vector hitLocation, hitNormal; local Vector extent; local bool bIsThrown; local float elevation; local bool bSafe; if (target == None) return false; if (target.bIgnore) return false; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon == None) return false; if (bCheckReadiness && !dxWeapon.bReadyToFire) return false; if (dxWeapon.ReloadCount > 0) { if (dxWeapon.AmmoType == None) return false; if (dxWeapon.AmmoType.AmmoAmount <= 0) return false; } if (FireElevation > 0) { elevation = FireElevation + (CollisionHeight+target.CollisionHeight); if (elevation < 10) elevation = 10; if (Abs(Location.Z-target.Location.Z) > elevation) return false; } bIsThrown = IsThrownWeapon(dxWeapon); extraDist = target.CollisionRadius; //extraDist = 0; GetPawnWeaponRanges(self, tempMinRange, tempMaxRange, temp); if (bDiscountMinRange) tempMinRange = 0; if (tempMinRange >= tempMaxRange) return false; ViewRotation = Rotation; GetAxes(ViewRotation, X, Y, Z); projStart = dxWeapon.ComputeProjectileStart(X, Y, Z); if (bLeadTarget && !dxWeapon.bInstantHit && (dxWeapon.ProjectileSpeed > 0)) { if (bIsThrown) { // compute target's position 1.5 seconds in the future projEnd = target.Location + (target.Velocity*1.5); } else { // projEnd = target.Location + (target.Velocity*dist/dxWeapon.ProjectileSpeed); if (!ComputeTargetLead(target, projStart, dxWeapon.ProjectileSpeed, 5.0, projEnd)) return false; } } else projEnd = target.Location; if (bIsThrown) projEnd += vect(0,0,-1)*(target.CollisionHeight-5); dist = VSize(projEnd - Location); if (dist < 0) dist = 0; if ((dist < tempMinRange) || (dist-extraDist > tempMaxRange)) return false; if (!bIsThrown) { bSafe = FastTrace(target.Location, projStart); if (!bSafe && target.bIsPlayer) // players only... hack { projEnd += vect(0,0,1)*target.BaseEyeHeight; bSafe = FastTrace(target.Location + vect(0,0,1)*target.BaseEyeHeight, projStart); } if (!bSafe) return false; } if (dxWeapon.bInstantHit) return (AISafeToShoot(hitActor, projEnd, projStart, , true)); else { extent.X = dxWeapon.ProjectileClass.default.CollisionRadius; extent.Y = dxWeapon.ProjectileClass.default.CollisionRadius; extent.Z = dxWeapon.ProjectileClass.default.CollisionHeight; if (bIsThrown && (throwAccuracy > 0)) return (AISafeToThrow(projEnd, projStart, throwAccuracy, extent)); else if (dxWeapon.IsA('WeaponPlasmaRifle')) //CyberP: so plasma rifle users can attack close range return (AISafeToShoot(hitActor, projEnd, projStart, extent*1.1)); else return (AISafeToShoot(hitActor, projEnd, projStart, extent*3)); } } // ---------------------------------------------------------------------- // ComputeTargetLead() // ---------------------------------------------------------------------- function bool ComputeTargetLead(pawn target, vector projectileStart, float projectileSpeed, float maxTime, out Vector hitPos) { local vector targetLoc; local vector targetVel; local float termA, termB, termC; local float temp; local float base, range; local float time1, time2; local bool bSuccess; bSuccess = true; targetLoc = target.Location - projectileStart; targetVel = target.Velocity; if (target.Physics == PHYS_Falling) targetVel.Z = 0; // Given a target position and velocity, and a projectile speed, // compute the position at which a projectile will hit the // target if the target continues at its current velocity // (Warning: messy computations follow. I can't believe I remembered // enough algebra to figure this out on my own... :) termA = targetVel.X*targetVel.X + targetVel.Y*targetVel.Y + targetVel.Z*targetVel.Z - projectileSpeed*projectileSpeed; termB = 2*targetLoc.X*targetVel.X + 2*targetLoc.Y*targetVel.Y + 2*targetLoc.Z*targetVel.Z; termC = targetLoc.X*targetLoc.X + targetLoc.Y*targetLoc.Y + targetLoc.Z*targetLoc.Z; if ((termA < 0.000001) && (termA > -0.000001)) // avoid divide-by-zero errors... termA = 0.000001; // fudge a little when velocities are equal temp = termB*termB - 4*termA*termC; if (temp < 0) bSuccess = false; if (bSuccess) { base = -termB/(2*termA); range = sqrt(temp)/(2*termA); time1 = base+range; time2 = base-range; if ((time1 > time2) || (time1 < 0)) // best time first time1 = time2; if ((time1 < 0) || (time1 >= maxTime)) bSuccess = false; } if (bSuccess) hitPos = target.Location + target.Velocity*time1; return (bSuccess); } // ---------------------------------------------------------------------- // GetPawnWeaponRanges() // ---------------------------------------------------------------------- function GetPawnWeaponRanges(Pawn other, out float minRange, out float maxAccurateRange, out float maxRange) { local DeusExWeapon pawnWeapon; local Class<DeusExProjectile> projectileClass; pawnWeapon = DeusExWeapon(other.Weapon); if (pawnWeapon != None) { pawnWeapon.GetWeaponRanges(minRange, maxAccurateRange, maxRange); if (IsThrownWeapon(pawnWeapon)) // hack minRange = 0; } else { minRange = 0; maxAccurateRange = other.CollisionRadius; maxRange = maxAccurateRange; } if (maxAccurateRange > maxRange) maxAccurateRange = maxRange; if (minRange > maxRange) minRange = maxRange; } // ---------------------------------------------------------------------- // GetWeaponBestRange() // ---------------------------------------------------------------------- function GetWeaponBestRange(DeusExWeapon dxWeapon, out float bestRangeMin, out float bestRangeMax) { local float temp; local float minRange, maxRange; local float AIMinRange, AIMaxRange; if (dxWeapon != None) { dxWeapon.GetWeaponRanges(minRange, maxRange, temp); if (IsThrownWeapon(dxWeapon)) // hack minRange = 0; AIMinRange = dxWeapon.AIMinRange; AIMaxRange = dxWeapon.AIMaxRange; if ((AIMinRange > 0) && (AIMinRange >= minRange) && (AIMinRange <= maxRange)) bestRangeMin = AIMinRange; else bestRangeMin = minRange; if ((AIMaxRange > 0) && (AIMaxRange >= minRange) && (AIMaxRange <= maxRange)) bestRangeMax = AIMaxRange; else bestRangeMax = maxRange; if (bestRangeMin > bestRangeMax) bestRangeMin = bestRangeMax; } else { bestRangeMin = 0; bestRangeMax = 0; } } // ---------------------------------------------------------------------- // ReadyForNewEnemy() // ---------------------------------------------------------------------- function bool ReadyForNewEnemy() { if ((Enemy == None) || (EnemyTimer > 5.0)) return True; else return False; } // ---------------------------------------------------------------------- // CheckEnemyParams() [internal use only] // ---------------------------------------------------------------------- function CheckEnemyParams(Pawn checkPawn, out Pawn bestPawn, out int bestThreatLevel, out float bestDist) { local ScriptedPawn sPawn; local bool bReplace; local float dist; local int threatLevel; local bool bValid; bValid = IsValidEnemy(checkPawn); if (bValid && (Enemy != checkPawn)) { // Honor cloaking, radar transparency, and other augs if this guy isn't our current enemy if (ComputeActorVisibility(checkPawn) < 0.1) bValid = false; } if (bValid) { sPawn = ScriptedPawn(checkPawn); dist = VSize(checkPawn.Location - Location); if (checkPawn.IsA('Robot')) dist *= 0.5; // arbitrary if (Enemy == checkPawn) dist *= 0.75; // arbitrary if (sPawn != None) { if (sPawn.bAttacking) { if (sPawn.Enemy == self) threatLevel = 2; else threatLevel = 1; } else if (sPawn.GetStateName() == 'Alerting') threatLevel = 3; else if ((sPawn.GetStateName() == 'Fleeing') || (sPawn.GetStateName() == 'Burning')) threatLevel = 0; else if (sPawn.Weapon != None) threatLevel = 1; else threatLevel = 0; } else // player { if (checkPawn.Weapon != None) threatLevel = 2; else threatLevel = 1; } bReplace = false; if (bestPawn == None) bReplace = true; else if (bestThreatLevel < threatLevel) bReplace = true; else if (bestDist > dist) bReplace = true; if (bReplace) { if ((Enemy == checkPawn) || (AICanSee(checkPawn, , false, false, true, true) > 0)) { bestPawn = checkPawn; bestThreatLevel = threatLevel; bestDist = dist; } } } } // ---------------------------------------------------------------------- // FindBestEnemy() // ---------------------------------------------------------------------- function FindBestEnemy(bool bIgnoreCurrentEnemy) { local Pawn nextPawn; local Pawn bestPawn; local float bestDist; local int bestThreatLevel; local float newSeenTime; bestPawn = None; bestDist = 0; bestThreatLevel = 0; if (!bIgnoreCurrentEnemy && (Enemy != None)) CheckEnemyParams(Enemy, bestPawn, bestThreatLevel, bestDist); foreach RadiusActors(Class'Pawn', nextPawn, 2000) // arbitrary if (enemy != nextPawn) CheckEnemyParams(nextPawn, bestPawn, bestThreatLevel, bestDist); if (bestPawn != Enemy) newSeenTime = 0; else newSeenTime = EnemyLastSeen; SetEnemy(bestPawn, newSeenTime, true); EnemyTimer = 0; } // ---------------------------------------------------------------------- // ShouldStrafe() // ---------------------------------------------------------------------- function bool ShouldStrafe() { // This may be overridden from subclasses //return (AICanSee(enemy, 1.0, false, true, true, true) > 0); return (AICanShoot(enemy, false, false, 0.025, true)); } // ---------------------------------------------------------------------- // ShouldFlee() // ---------------------------------------------------------------------- function bool ShouldFlee() { // This may be overridden from subclasses if (MinHealth > 0) { if (Health <= MinHealth) return true; else if (HealthArmLeft <= 0) return true; else if (HealthArmRight <= 0) return true; else if (HealthLegLeft <= 0) return true; else if (HealthLegRight <= 0) return true; else return false; } else return false; } // ---------------------------------------------------------------------- // ShouldDropWeapon() // ---------------------------------------------------------------------- function bool ShouldDropWeapon() { if (isA('MIB')||isA('WIB')) return false; if (((HealthArmLeft <= 0) || (HealthArmRight <= 0)) && (Health > 0)) return true; else return false; } // ---------------------------------------------------------------------- // TryLocation() // ---------------------------------------------------------------------- function bool TryLocation(out vector position, optional float minDist, optional bool bTraceActors, optional NearbyProjectileList projList) { local float magnitude; local vector normalPos; local Rotator rot; local float dist; local bool bSuccess; normalPos = position-Location; magnitude = VSize(normalPos); if (minDist > magnitude) minDist = magnitude; rot = Rotator(position-Location); bSuccess = AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, position); if (bSuccess) { if (bDefendHome && !IsNearHome(position)) bSuccess = false; else if (bAvoidHarm && IsLocationDangerous(projList, position)) bSuccess = false; } return (bSuccess); } // ---------------------------------------------------------------------- // ComputeBestFiringPosition() // ---------------------------------------------------------------------- function EDestinationType ComputeBestFiringPosition(out vector newPosition) { local float selfMinRange, selfMaxRange; local float enemyMinRange, enemyMaxRange; local float temp; local float dist; local float innerRange[2], outerRange[2]; local Rotator relativeRotation; local float hAngle, vAngle; local int acrossDist; local float awayDist; local float extraDist; local float fudgeMargin; local int angle; local float maxDist; local float distDelta; local bool bInnerValid, bOuterValid; local vector tryVector; local EDestinationType destType; local float moveMult; local float reloadMult; local float minArea; local float minDist; local float range; local float margin; local NearbyProjectileList projList; local vector projVector; local bool bUseProjVector; local rotator sprintRot; local vector sprintVect; local bool bUseSprint; destType = DEST_Failure; extraDist = enemy.CollisionRadius*0.5; fudgeMargin = 100; minArea = 35; GetPawnWeaponRanges(self, selfMinRange, selfMaxRange, temp); GetPawnWeaponRanges(enemy, enemyMinRange, temp, enemyMaxRange); if (selfMaxRange > 1200) selfMaxRange = 1200; if (enemyMaxRange > 1200) enemyMaxRange = 1200; // hack, to prevent non-strafing NPCs from trying to back up if (!bCanStrafe) selfMinRange = 0; minDist = enemy.CollisionRadius + CollisionRadius - (extraDist+1); if (selfMinRange < minDist) selfMinRange = minDist; if (selfMinRange < MinRange) selfMinRange = MinRange; if (selfMaxRange > MaxRange) selfMaxRange = MaxRange; dist = VSize(enemy.Location-Location); innerRange[0] = selfMinRange; innerRange[1] = selfMaxRange; outerRange[0] = selfMinRange; outerRange[1] = selfMaxRange; // hack, to prevent non-strafing NPCs from trying to back up if (selfMaxRange > enemyMinRange) innerRange[1] = enemyMinRange; if ((selfMinRange < enemyMaxRange) && bCanStrafe) // hack, to prevent non-strafing NPCs from trying to back up outerRange[0] = enemyMaxRange; range = outerRange[1]-outerRange[0]; if (range < minArea) { outerRange[0] = 0; outerRange[1] = 0; } range = innerRange[1]-innerRange[0]; if (range < minArea) { innerRange[0] = outerRange[0]; innerRange[1] = outerRange[1]; outerRange[0] = 0; outerRange[1] = 0; } // If the enemy can reach us through our entire weapon range, just use the range if ((innerRange[0] >= innerRange[1]) && (outerRange[0] >= outerRange[1])) { innerRange[0] = selfMinRange; innerRange[1] = selfMaxRange; } innerRange[0] += extraDist; innerRange[1] += extraDist; outerRange[0] += extraDist; outerRange[1] += extraDist; if (innerRange[0] >= innerRange[1]) bInnerValid = false; else bInnerValid = true; if (outerRange[0] >= outerRange[1]) bOuterValid = false; else bOuterValid = true; if (!bInnerValid) { // ugly newPosition = Location; // return DEST_SameLocation; return destType; } relativeRotation = Rotator(Location - enemy.Location); hAngle = (relativeRotation.Yaw - enemy.Rotation.Yaw) & 65535; if (hAngle > 32767) hAngle -= 65536; // ignore vertical angle for now awayDist = dist; acrossDist = 0; maxDist = GroundSpeed*0.6; // distance covered in 6/10 second if (bInnerValid) { margin = (innerRange[1]-innerRange[0]) * 0.5; if (margin > fudgeMargin) margin = fudgeMargin; if (awayDist < innerRange[0]) awayDist = innerRange[0]+margin; else if (awayDist > innerRange[1]) awayDist = innerRange[1]-margin; } if (bOuterValid) { margin = (outerRange[1]-outerRange[0]) * 0.5; if (margin > fudgeMargin) margin = fudgeMargin; if (awayDist > outerRange[1]) awayDist = outerRange[1]-margin; } if (awayDist > dist+maxDist) awayDist = dist+maxDist; if (awayDist < dist-maxDist) awayDist = dist-maxDist; // Used to determine whether NPCs should sprint/avoid aim moveMult = 1.0; if ((dist <= 512) && enemy.bIsPlayer && (enemy.Weapon != None) && (enemyMaxRange < 180)) moveMult = CloseCombatMult; if (bAvoidAim && !enemy.bIgnore && (FRand() <= AvoidAccuracy*moveMult)) { if ((awayDist < enemyMaxRange+maxDist+50) && (awayDist < 800) && (Enemy.Weapon != None)) { if (dist > 0) angle = int(atan(CollisionRadius*2.0/dist)*32768/Pi); else angle = 16384; if ((hAngle >= -angle) && (hAngle <= angle)) { if (hAngle < 0) acrossDist = (-angle-hAngle)-128; else acrossDist = (angle-hAngle)+128; if (Rand(20) == 0) acrossDist = -acrossDist; } } } // projList is implicitly initialized to null... bUseProjVector = false; if (bAvoidHarm && (FRand() <= HarmAccuracy)) { if (GetProjectileList(projList, Location) > 0) { if (IsLocationDangerous(projList, Location)) { projVector = ComputeAwayVector(projList); bUseProjVector = true; } } } reloadMult = 1.0; if (IsWeaponReloading() && Enemy.bIsPlayer) reloadMult = 0.5; bUseSprint = false; if (!bUseProjVector && bSprint && bCanStrafe && !enemy.bIgnore && (FRand() <= SprintRate*0.5*moveMult*reloadMult)) { if (bOuterValid || (innerRange[1] > 100)) // sprint on long-range weapons only { sprintRot = Rotator(enemy.Location - Location); if (Rand(2) == 1) sprintRot.Yaw += 16384; else sprintRot.Yaw += 49152; sprintRot = RandomBiasedRotation(sprintRot.Yaw, 0.5, 0, 0); sprintRot.Pitch = 0; sprintVect = Vector(sprintRot)*GroundSpeed*(FRand()+0.5); bUseSprint = true; } } if ((acrossDist != 0) || (awayDist != dist) || bUseProjVector || bUseSprint) { if (Rand(40) != 0) { if ((destType == DEST_Failure) && bUseProjVector) { tryVector = projVector + Location; if (TryLocation(tryVector, CollisionRadius+16)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && (acrossDist != 0) && (awayDist != dist)) { tryVector = Vector(relativeRotation+(rot(0, 1, 0)*acrossDist))*awayDist + enemy.Location; if (TryLocation(tryVector, CollisionRadius+16, , projList)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && (awayDist != dist)) { tryVector = Vector(relativeRotation)*awayDist + enemy.Location; if (TryLocation(tryVector, CollisionRadius+16, , projList)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && (acrossDist != 0)) { tryVector = Vector(relativeRotation+(rot(0, 1, 0)*acrossDist))*dist + enemy.Location; if (TryLocation(tryVector, CollisionRadius+16, , projList)) destType = DEST_NewLocation; } if ((destType == DEST_Failure) && bUseSprint) { tryVector = sprintVect + Location; if (TryLocation(tryVector, CollisionRadius+16)) destType = DEST_NewLocation; } } if (destType == DEST_Failure) { if ((moveMult >= 0.5) || (FRand() <= moveMult)) { if (AIPickRandomDestination(CollisionRadius+16, maxDist, relativeRotation.Yaw+32768, 0.6, -relativeRotation.Pitch, 0.6, 2, 0.9, tryVector)) if (!bDefendHome || IsNearHome(tryVector)) if (!bAvoidHarm || !IsLocationDangerous(projList, tryVector)) destType = DEST_NewLocation; } else destType = DEST_SameLocation; } if (destType != DEST_Failure) newPosition = tryVector; } else destType = DEST_SameLocation; return destType; } // ---------------------------------------------------------------------- // SetAttackAngle() // // Sets the angle from which an asynchronous attack will occur // (hack needed for DeusExWeapon) // ---------------------------------------------------------------------- function SetAttackAngle() { local bool bCanShoot; bCanShoot = false; if (Enemy != None) if (AICanShoot(Enemy, true, false, 0.025)) bCanShoot = true; if (!bCanShoot) ViewRotation = Rotation; } // ---------------------------------------------------------------------- // AdjustAim() // // Adjust the aim at target // ---------------------------------------------------------------------- function rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget) { local rotator FireRotation; local vector FireSpot; local actor HitActor; local vector HitLocation, HitNormal; local vector vectorArray[3]; local vector tempVector; local int i; local int swap; local Rotator rot; local bool bIsThrown; local DeusExMover dxMover; local actor Target; // evil fix -- STM bIsThrown = IsThrownWeapon(DeusExWeapon(Weapon)); // took this line out for evil fix... // if ( Target == None ) Target = Enemy; if ( Target == None ) return Rotation; if ( !Target.IsA('Pawn') ) return rotator(Target.Location - Location); FireSpot = Target.Location; if (leadTarget && !IsA('Gray') && FRand() < 0.7 && (projSpeed > 0)) //CyberP: grays never target lead { if (bIsThrown) { // compute target's position 1.5 seconds in the future FireSpot = target.Location + (target.Velocity*1.5); } else { //FireSpot += (Target.Velocity * VSize(Target.Location - ProjStart)/projSpeed); ComputeTargetLead(Pawn(Target), ProjStart, projSpeed, 20.0, FireSpot); } } else //CyberP: don't always target lead for variation { FireSpot = target.Location; } if (bIsThrown) { vectorArray[0] = FireSpot - vect(0,0,1)*(Target.CollisionHeight-5); // floor vectorArray[1] = vectorArray[0] + Vector(rot(0,1,0)*Rand(65536))*CollisionRadius*1.2; vectorArray[2] = vectorArray[0] + Vector(rot(0,1,0)*Rand(65536))*CollisionRadius*1.2; for (i=0; i<3; i++) { if (AISafeToThrow(vectorArray[i], ProjStart, 0.025)) break; } if (i < 3) { FireSpot = vectorArray[i]; FireRotation = ViewRotation; } else FireRotation = Rotator(FireSpot - ProjStart); } else { dxMover = DeusExMover(Target.Base); if ((dxMover != None) && dxMover.bBreakable) { tempVector = Normal((Location-Target.Location)*vect(1,1,0))*(Target.CollisionRadius*1.01) - vect(0,0,1)*(Target.CollisionHeight*1.01); vectorArray[0] = FireSpot + tempVector; } else if (bAimForHead) vectorArray[0] = FireSpot + vect(0,0,1)*(Target.CollisionHeight*0.85); // head else vectorArray[0] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight); vectorArray[1] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight); vectorArray[2] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight); for (i=0; i<3; i++) { if (AISafeToShoot(HitActor, vectorArray[i], ProjStart)) break; } if (i < 3) FireSpot = vectorArray[i]; FireRotation = Rotator(FireSpot - ProjStart); } if (warnTarget && Pawn(Target) != None) Pawn(Target).WarnTarget(self, projSpeed, vector(FireRotation)); FireRotation.Yaw = FireRotation.Yaw & 65535; if ( (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) > 8192) && (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) < 57343) ) { if ( (FireRotation.Yaw > Rotation.Yaw + 32768) || ((FireRotation.Yaw < Rotation.Yaw) && (FireRotation.Yaw > Rotation.Yaw - 32768)) ) FireRotation.Yaw = Rotation.Yaw - 8192; else FireRotation.Yaw = Rotation.Yaw + 8192; } viewRotation = FireRotation; return FireRotation; } // ---------------------------------------------------------------------- // IsThrownWeapon() // ---------------------------------------------------------------------- function bool IsThrownWeapon(DeusExWeapon testWeapon) { local Class<ThrownProjectile> throwClass; local bool bIsThrown; bIsThrown = false; if (testWeapon != None) { if (!testWeapon.bInstantHit) { throwClass = class<ThrownProjectile>(testWeapon.ProjectileClass); if (throwClass != None) bIsThrown = true; } } return bIsThrown; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // CALLBACKS AND OVERRIDDEN FUNCTIONS // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // Tick() // ---------------------------------------------------------------------- function Tick(float deltaTime) { local float dropPeriod; local float adjustedRate; local DeusExPlayer player; local name stateName; local vector loc; local bool bDoLowPriority; local bool bCheckOther; local bool bCheckPlayer; local float ModDistance; player = DeusExPlayer(GetPlayerPawn()); bDoLowPriority = true; bCheckPlayer = true; bCheckOther = true; if (bTickVisibleOnly) { if (DistanceFromPlayer > 1200) bDoLowPriority = false; if (player !=None && player.CombatDifficulty > 2) //CyberP: realistic & hardcore have realistic vision ModDistance=4096.0; else ModDistance=2590.0; if (DistanceFromPlayer > ModDistance) bCheckPlayer = false; if ((DistanceFromPlayer > 600) && (LastRendered() >= 5.0)) bCheckOther = false; } /* if (bDisappear && (InStasis() || (LastRendered() > 5.0))) { Destroy(); return; } if (PrePivotTime > 0) { if (deltaTime < PrePivotTime) { PrePivot = PrePivot + (DesiredPrePivot-PrePivot)*deltaTime/PrePivotTime; PrePivotTime -= deltaTime; } else { PrePivot = DesiredPrePivot; PrePivotTime = 0; } } if (bDoLowPriority) Super.Tick(deltaTime); UpdateAgitation(deltaTime); UpdateFear(deltaTime); AlarmTimer -= deltaTime; if (AlarmTimer < 0) AlarmTimer = 0; if (Weapon != None) WeaponTimer += deltaTime; else if (WeaponTimer != 0) WeaponTimer = 0; if ((ReloadTimer > 0) && (Weapon != None)) ReloadTimer -= deltaTime; else ReloadTimer = 0; if (AvoidWallTimer > 0) { AvoidWallTimer -= deltaTime; if (AvoidWallTimer < 0) AvoidWallTimer = 0; } if (AvoidBumpTimer > 0) { AvoidBumpTimer -= deltaTime; if (AvoidBumpTimer < 0) AvoidBumpTimer = 0; } if (CloakEMPTimer > 0) { CloakEMPTimer -= deltaTime; if (CloakEMPTimer < 0) CloakEMPTimer = 0; } if (TakeHitTimer > 0) { TakeHitTimer -= deltaTime; if (TakeHitTimer < 0) TakeHitTimer = 0; } if (CarcassCheckTimer > 0) { CarcassCheckTimer -= deltaTime; if (CarcassCheckTimer < 0) CarcassCheckTimer = 0; } if (PotentialEnemyTimer > 0) { PotentialEnemyTimer -= deltaTime; if (PotentialEnemyTimer <= 0) { PotentialEnemyTimer = 0; PotentialEnemyAlliance = ''; } } if (BeamCheckTimer > 0) { BeamCheckTimer -= deltaTime; if (BeamCheckTimer < 0) BeamCheckTimer = 0; } if (FutzTimer > 0) { FutzTimer -= deltaTime; if (FutzTimer < 0) FutzTimer = 0; } if (PlayerAgitationTimer > 0) { PlayerAgitationTimer -= deltaTime; if (PlayerAgitationTimer < 0) PlayerAgitationTimer = 0; } if (DistressTimer >= 0) { DistressTimer += deltaTime; if (DistressTimer > FearSustainTime) DistressTimer = -1; } if (bHasCloak) EnableCloak(Health <= CloakThreshold); if (bAdvancedTactics) { if ((Acceleration == vect(0,0,0)) || (Physics != PHYS_Walking) || (TurnDirection == TURNING_None)) { bAdvancedTactics = false; if (TurnDirection != TURNING_None) MoveTimer -= 4.0; ActorAvoiding = None; NextDirection = TURNING_None; TurnDirection = TURNING_None; bClearedObstacle = true; ObstacleTimer = 0; } } if (bOnFire) { burnTimer += deltaTime; if (burnTimer >= BurnPeriod) ExtinguishFire(); } if (bDoLowPriority) { if ((bleedRate > 0) && bCanBleed) { adjustedRate = (1.0-FClamp(bleedRate, 0.0, 1.0))*1.0+0.1; // max 10 drops per second dropPeriod = adjustedRate / FClamp(VSize(Velocity)/512.0, 0.05, 1.0); dropCounter += deltaTime; while (dropCounter >= dropPeriod) { SpurtBlood(); dropCounter -= dropPeriod; } bleedRate -= deltaTime/clotPeriod; } if (bleedRate <= 0) { dropCounter = 0; bleedRate = 0; } } */ if (bStandInterpolation) UpdateStanding(deltaTime); // this is UGLY! if (bOnFire && (health > 0)) { stateName = GetStateName(); if ((stateName != 'Burning') && (stateName != 'TakingHit') && (stateName != 'RubbingEyes')) GotoState('Burning'); } else { if (bDoLowPriority) { // Don't allow radius-based convos to interupt other conversations! if ((player != None) && (GetStateName() != 'Conversation') && (GetStateName() != 'FirstPersonConversation')) player.StartConversation(Self, IM_Radius); } if (CheckEnemyPresence(deltaTime, bCheckPlayer, bCheckOther)) HandleEnemy(); else { CheckBeamPresence(deltaTime); if (bDoLowPriority || LastRendered() < 5.0) CheckCarcassPresence(deltaTime); // hacky -- may change state! } } // Randomly spawn an air bubble every 0.2 seconds if we're underwater if (HeadRegion.Zone.bWaterZone && bSpawnBubbles && bDoLowPriority) { swimBubbleTimer += deltaTime; if (swimBubbleTimer >= 0.2) { swimBubbleTimer = 0; if (FRand() < 0.4) { loc = Location + VRand() * 4; loc.Z += CollisionHeight * 0.9; Spawn(class'AirBubble', Self,, loc); } } } // Handle poison damage UpdatePoison(deltaTime); } // ---------------------------------------------------------------------- // SpurtBlood() // ---------------------------------------------------------------------- function SpurtBlood() { local vector bloodVector; bloodVector = vect(0,0,1)*CollisionHeight*0.5; // so folks don't bleed from the crotch spawn(Class'BloodDrop',,,bloodVector+Location); } // ---------------------------------------------------------------------- // TakeDamage() // ---------------------------------------------------------------------- function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, true); } // ---------------------------------------------------------------------- // Timer() // ---------------------------------------------------------------------- function Timer() { if (bCloakOn) //CyberP: for new cloaking effect. { if (IsA('SecurityBot3')) SetSkinStyle(STY_Translucent, Texture'HDTPWeaponCrowbarTex2', 0.4); else SetSkinStyle(STY_Translucent, Texture'HDTPWeaponCrowbarTex2', 0.15); LightRadius = 0; AmbientGlow = 0; } else { UpdateFire(); } } // ---------------------------------------------------------------------- // ZoneChange() // ---------------------------------------------------------------------- function ZoneChange(ZoneInfo newZone) { local vector jumpDir; if (!bInWorld) return; if (newZone.bWaterZone) { EnableShadow(false); if (IsA('Doberman')) //CyberP: Doberman have no swim animations, so kill them. TakeDamage(100,self,vect(0,0,0),vect(0,0,0),'Shot'); if (!bCanSwim) MoveTimer = -1.0; else if (Physics != PHYS_Swimming) { if (bOnFire) ExtinguishFire(); PlaySwimming(); setPhysics(PHYS_Swimming); } swimBubbleTimer = 0; } else if (Physics == PHYS_Swimming) { EnableShadow(true); if ( bCanFly ) SetPhysics(PHYS_Flying); else { SetPhysics(PHYS_Falling); if ( bCanWalk && (Abs(Acceleration.X) + Abs(Acceleration.Y) > 0) && CheckWaterJump(jumpDir) ) JumpOutOfWater(jumpDir); } } } // ---------------------------------------------------------------------- // BaseChange() // ---------------------------------------------------------------------- singular function BaseChange() { Super.BaseChange(); if (bSitting && !IsSeatValid(SeatActor)) { StandUp(); if (GetStateName() == 'Sitting') GotoState('Sitting', 'Begin'); } } // ---------------------------------------------------------------------- // PainTimer() // ---------------------------------------------------------------------- event PainTimer() { local float depth; local PointRegion painRegion; if ((Health <= 0) || (Level.NetMode == NM_Client)) return; painRegion = HeadRegion; if (!painRegion.Zone.bPainZone || (painRegion.Zone.DamageType == ReducedDamageType)) painRegion = Region; if (!painRegion.Zone.bPainZone || (painRegion.Zone.DamageType == ReducedDamageType)) painRegion = FootRegion; if (painRegion.Zone.bPainZone && (painRegion.Zone.DamageType != ReducedDamageType)) { depth = 0; if (FootRegion.Zone.bPainZone) depth += 0.3; if (Region.Zone.bPainZone) depth += 0.3; if (HeadRegion.Zone.bPainZone) depth += 0.4; if (painRegion.Zone.DamagePerSec > 0) TakeDamage(int(float(painRegion.Zone.DamagePerSec) * depth), None, Location, vect(0,0,0), painRegion.Zone.DamageType); // took out healing for NPCs -- we don't use healing zones anyway /* else if ( Health < Default.Health ) Health = Min(Default.Health, Health - depth * FootRegion.Zone.DamagePerSec); */ if (Health > 0) PainTime = 1.0; } else if (HeadRegion.Zone.bWaterZone && (UnderWaterTime > 0)) { TakeDamage(5, None, Location, vect(0,0,0), 'Drowned'); if (Health > 0) PainTime = 2.0; } } // ---------------------------------------------------------------------- // CheckWaterJump() // ---------------------------------------------------------------------- function bool CheckWaterJump(out vector WallNormal) { local actor HitActor; local vector HitLocation, HitNormal, checkpoint, start, checkNorm, Extent; if (CarriedDecoration != None) return false; checkpoint = vector(Rotation); checkpoint.Z = 0.0; checkNorm = Normal(checkpoint); checkPoint = Location + CollisionRadius * checkNorm; Extent = CollisionRadius * vect(1,1,0); Extent.Z = CollisionHeight; HitActor = Trace(HitLocation, HitNormal, checkpoint, Location, false, Extent); if ( (HitActor != None) && (Pawn(HitActor) == None) ) { WallNormal = -1 * HitNormal; start = Location; start.Z += 1.1 * MaxStepHeight + CollisionHeight; checkPoint = start + 2 * CollisionRadius * checkNorm; HitActor = Trace(HitLocation, HitNormal, checkpoint, start, true, Extent); if (HitActor == None) return true; } return false; } // ---------------------------------------------------------------------- // SetMovementPhysics() // ---------------------------------------------------------------------- function SetMovementPhysics() { // re-implement SetMovementPhysics() in subclass for flying and swimming creatures if (Physics == PHYS_Falling) return; if (Region.Zone.bWaterZone && bCanSwim) SetPhysics(PHYS_Swimming); else if (Default.Physics == PHYS_None) SetPhysics(PHYS_Walking); else SetPhysics(Default.Physics); } // ---------------------------------------------------------------------- // PreSetMovement() // ---------------------------------------------------------------------- function PreSetMovement() { // Copied from Pawn.uc and overridden so Pawn doesn't erase our walking/swimming/flying physics if (JumpZ > 0) bCanJump = true; // No, no, no!!! /* bCanWalk = true; bCanSwim = false; bCanFly = false; MinHitWall = -0.6; if (Intelligence > BRAINS_Reptile) bCanOpenDoors = true; if (Intelligence == BRAINS_Human) bCanDoSpecial = true; */ } // ---------------------------------------------------------------------- // ChangedWeapon() // ---------------------------------------------------------------------- function ChangedWeapon() { // do nothing } // ---------------------------------------------------------------------- // SwitchToBestWeapon() // ---------------------------------------------------------------------- function bool SwitchToBestWeapon() { local Inventory inv; local DeusExWeapon curWeapon; local float score; local DeusExWeapon dxWeapon; local DeusExWeapon bestWeapon; local float bestScore; local int fallbackLevel; local int curFallbackLevel; local bool bBlockSpecial; local bool bValid; local bool bWinner; local float minRange, accRange; local float range, centerRange; local float cutoffRange; local float enemyRange; local float minEnemy, accEnemy, maxEnemy; local ScriptedPawn enemyPawn; local Robot enemyRobot; local DeusExPlayer enemyPlayer; local float enemyRadius; local bool bEnemySet; local int loopCount, i; // hack - check for infinite inventory local Inventory loopInv; // hack - check for infinite inventory if (ShouldDropWeapon()) { DropWeapon(); return false; } bBlockSpecial = false; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) { if (dxWeapon.AITimeLimit > 0) { if (SpecialTimer <= 0) { bBlockSpecial = true; FireTimer = dxWeapon.AIFireDelay; } } } bestWeapon = None; bestScore = 0; fallbackLevel = 0; inv = Inventory; bEnemySet = false; minEnemy = 0; accEnemy = 0; enemyRange = 400; // default enemyRadius = 0; enemyPawn = None; enemyRobot = None; if (Enemy != None) { bEnemySet = true; enemyRange = VSize(Enemy.Location - Location); enemyRadius = Enemy.CollisionRadius; if (DeusExWeapon(Enemy.Weapon) != None) DeusExWeapon(Enemy.Weapon).GetWeaponRanges(minEnemy, accEnemy, maxEnemy); enemyPawn = ScriptedPawn(Enemy); enemyRobot = Robot(Enemy); enemyPlayer = DeusExPlayer(Enemy); } loopCount = 0; while (inv != None) { // THIS IS A MAJOR HACK!!! loopCount++; if (loopCount == 9999) { log("********** RUNAWAY LOOP IN SWITCHTOBESTWEAPON ("$self$") **********"); loopInv = Inventory; i = 0; while (loopInv != None) { i++; if (i > 300) break; log(" Inventory "$i$" - "$loopInv); loopInv = loopInv.Inventory; } } curWeapon = DeusExWeapon(inv); if (curWeapon != None) { bValid = true; if (curWeapon.ReloadCount > 0) { if (curWeapon.AmmoType == None) bValid = false; else if (curWeapon.AmmoType.AmmoAmount < 1) bValid = false; } // Ensure we can actually use this weapon here if (bValid) { // lifted from DeusExWeapon... if ((curWeapon.EnviroEffective == ENVEFF_Air) || (curWeapon.EnviroEffective == ENVEFF_Vacuum) || (curWeapon.EnviroEffective == ENVEFF_AirVacuum)) if (curWeapon.Region.Zone.bWaterZone) bValid = false; } if (bValid) { GetWeaponBestRange(curWeapon, minRange, accRange); cutoffRange = minRange+(CollisionRadius+enemyRadius); range = (accRange - minRange) * 0.5; centerRange = minRange + range; if (range < 50) range = 50; if (enemyRange < centerRange) score = (centerRange - enemyRange)/range; else score = (enemyRange - centerRange)/range; if ((minRange >= minEnemy) && (accRange <= accEnemy)) score += 0.5; // arbitrary if ((cutoffRange >= enemyRange-CollisionRadius) && (cutoffRange >= 256)) // do not use long-range weapons on short-range targets score += 10000; curFallbackLevel = 3; if (curWeapon.bFallbackWeapon && !bUseFallbackWeapons) curFallbackLevel = 2; if (!bEnemySet && !curWeapon.bUseAsDrawnWeapon) curFallbackLevel = 1; if ((curWeapon.AIFireDelay > 0) && (FireTimer > 0)) curFallbackLevel = 0; if (bBlockSpecial && (curWeapon.AITimeLimit > 0) && (SpecialTimer <= 0)) curFallbackLevel = 0; // Adjust score based on opponent and damage type. // All damage types are listed here, even the ones that aren't used by weapons... :) // (hacky...) switch (curWeapon.WeaponDamageType()) { case 'Exploded': // Massive explosions are always good score -= 0.2; break; case 'Stunned': if (enemyPawn != None) { if (enemyPawn.bStunned) score += 1000; else score -= 1.5; } if (enemyPlayer != None) score += 10; break; case 'TearGas': if (enemyPawn != None) { if (enemyPawn.bStunned) //score += 1000; bValid = false; else score -= 5.0; } if (enemyRobot != None) //score += 10000; bValid = false; break; case 'HalonGas': if (enemyPawn != None) { if (enemyPawn.bStunned) //score += 1000; bValid = false; else if (enemyPawn.bOnFire) //score += 10000; bValid = false; else score -= 3.0; } if (enemyRobot != None) //score += 10000; bValid = false; break; case 'PoisonGas': case 'Poison': case 'PoisonEffect': case 'Radiation': if (enemyRobot != None) //score += 10000; bValid = false; break; case 'Flamed': if (enemyRobot != None && IsA('Robot')) //CyberP: bots don't use flamethrower on other bots score += 2000.0; break; case 'Burned': case 'Shot': if (enemyRobot != None) score += 0.5; else if (IsA('Greasel') && enemyRange < 320) score -= 2000.0; break; case 'Sabot': if (enemyRobot != None) score -= 0.5; break; case 'EMP': case 'NanoVirus': if (enemyRobot != None) score -= 10.0; else if (enemyPlayer != None) score -= 10.0; //CyberP: was 10.0 else //score += 10000; bValid = false; break; case 'Drowned': default: break; } // Special case for current weapon if ((curWeapon == Weapon) && (WeaponTimer < 10.0)) { // If we last changed weapons less than five seconds ago, // keep this weapon if (WeaponTimer < 5.0) score = -10; // If between five and ten seconds, use a sliding scale else score -= (10.0 - WeaponTimer)/5.0; } // Throw a little randomness into the computation... else { score += FRand()*0.1 - 0.05; if (score < 0) score = 0; } if (bValid) { // ugly if (bestWeapon == None) bWinner = true; else if (curFallbackLevel > fallbackLevel) bWinner = true; else if (curFallbackLevel < fallbackLevel) bWinner = false; else if (bestScore > score) bWinner = true; else bWinner = false; if (bWinner) { bestScore = score; bestWeapon = curWeapon; fallbackLevel = curFallbackLevel; } } } } inv = inv.Inventory; } // If we're changing weapons, reset the weapon timers if (Weapon != bestWeapon) { if (!bEnemySet) WeaponTimer = 10; // hack else WeaponTimer = 0; if (bestWeapon != None) if (bestWeapon.AITimeLimit > 0) SpecialTimer = bestWeapon.AITimeLimit; ReloadTimer = 0; } SetWeapon(bestWeapon); return false; } // ---------------------------------------------------------------------- // LoopBaseConvoAnim() // ---------------------------------------------------------------------- function LoopBaseConvoAnim() { // Special case for sitting if (bSitting) { if (!IsAnimating()) PlaySitting(); } // Special case for dancing else if (bDancing) { if (!IsAnimating()) PlayDancing(); } // Otherwise, just do the usual shit else Super.LoopBaseConvoAnim(); } // ---------------------------------------------------------------------- // BackOff() // ---------------------------------------------------------------------- function BackOff() { SetNextState(GetStateName(), 'ContinueFromDoor'); // MASSIVE hackage SetState('BackingOff'); } // ---------------------------------------------------------------------- // CheckDestLoc() // ---------------------------------------------------------------------- function CheckDestLoc(vector newDestLoc, optional bool bPathnode) { if (VSize(newDestLoc-LastDestLoc) <= 16) // too close DestAttempts++; else if (!IsPointInCylinder(self, newDestLoc)) DestAttempts++; else if (bPathnode && (VSize(newDestLoc-LastDestPoint) <= 16)) // too close DestAttempts++; else DestAttempts = 0; LastDestLoc = newDestLoc; if (bPathnode && (DestAttempts == 0)) LastDestPoint = newDestLoc; if (bEnableCheckDest && (DestAttempts >= 4)) //CyberP: hmm... BackOff(); } // ---------------------------------------------------------------------- // ResetDestLoc() // ---------------------------------------------------------------------- function ResetDestLoc() { DestAttempts = 0; LastDestLoc = vect(9999,9999,9999); // hack LastDestPoint = LastDestLoc; } // ---------------------------------------------------------------------- // EnableCheckDestLoc() // ---------------------------------------------------------------------- function EnableCheckDestLoc(bool bEnable) { // if (bEnableCheckDest && !bEnable) ResetDestLoc(); bEnableCheckDest = bEnable; } // ---------------------------------------------------------------------- // HandleTurn() // ---------------------------------------------------------------------- function bool HandleTurn(actor Other) { local bool bHandle; local bool bHackState; local DeusExDecoration dxDecoration; local ScriptedPawn scrPawn; // THIS ENTIRE SECTION IS A MASSIVE HACK TO GET AROUND PATHFINDING PROBLEMS // WHEN AN OBSTACLE COMPLETELY BLOCKS AN NPC'S PATH... bHandle = true; bHackState = false; if (bEnableCheckDest) { if (DestAttempts >= 2) { dxDecoration = DeusExDecoration(Other); scrPawn = ScriptedPawn(Other); if (dxDecoration != None) { if (!dxDecoration.bInvincible && !dxDecoration.bExplosive) { dxDecoration.HitPoints = 0; dxDecoration.TakeDamage(1, self, dxDecoration.Location, vect(0,0,0), 'Kick'); bHandle = false; } else if (DestAttempts >= 3) { bHackState = true; bHandle = false; } } else if (scrPawn != None) { if (DestAttempts >= 3) { if (scrPawn.GetStateName() != 'BackingOff') { bHackState = true; bHandle = false; } } } } if (bHackState) BackOff(); } return (bHandle); } // ---------------------------------------------------------------------- // Bump() // ---------------------------------------------------------------------- function Bump(actor Other) { local Rotator rot1, rot2; local int yaw; local ScriptedPawn avoidPawn; local DeusExPlayer dxPlayer; local bool bTurn; // Handle futzing and projectiles if (Other.Physics == PHYS_Falling) { if (DeusExProjectile(Other) != None) ReactToProjectiles(Other); else { dxPlayer = DeusExPlayer(Other.Instigator); if ((Other != dxPlayer) && (dxPlayer != None)) ReactToFutz(); } } // Have we walked into another (non-level) actor? bTurn = false; if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)) && bWalkAround && (Other != Level) && !Other.IsA('Mover')) if ((TurnDirection == TURNING_None) || (AvoidBumpTimer <= 0) && (Other.CollisionHeight > MaxStepHeight*0.75)) //CyberP: also check for small objects, which we ignore if (HandleTurn(Other)) bTurn = true; // Turn away from the actor if (bTurn) { // If we're not already turning, start if (TurnDirection == TURNING_None) { // Give ourselves a little extra time MoveTimer += 4.0; rot1 = Rotator(Other.Location-Location); // direction of object being bumped rot2 = Rotator(Acceleration); // direction we wish to go yaw = (rot2.Yaw - rot1.Yaw) & 65535; if (yaw > 32767) yaw -= 65536; // Depending on the angle we bump the actor, turn left or right if (yaw < 0) { TurnDirection = TURNING_Left; NextDirection = TURNING_Right; } else { TurnDirection = TURNING_Right; NextDirection = TURNING_Left; } bClearedObstacle = false; // Enable AlterDestination() bAdvancedTactics = true; } // Ignore multiple bumps in a row // BOOGER! Ignore same bump actor? if (AvoidBumpTimer <= 0) { AvoidBumpTimer = 0.2; ActorAvoiding = Other; bClearedObstacle = false; avoidPawn = ScriptedPawn(ActorAvoiding); // Avoid pairing off if (avoidPawn != None) { if ((avoidPawn.Acceleration != vect(0,0,0)) && (avoidPawn.Physics == PHYS_Walking) && (avoidPawn.TurnDirection != TURNING_None) && (avoidPawn.ActorAvoiding == self)) { if ((avoidPawn.TurnDirection == TURNING_Left) && (TurnDirection == TURNING_Right)) { TurnDirection = TURNING_Left; if (NextDirection != TURNING_None) NextDirection = TURNING_Right; } else if ((avoidPawn.TurnDirection == TURNING_Right) && (TurnDirection == TURNING_Left)) { TurnDirection = TURNING_Right; if (NextDirection != TURNING_None) NextDirection = TURNING_Left; } } } } } } // ---------------------------------------------------------------------- // HitWall() // ---------------------------------------------------------------------- function HitWall(vector HitLocation, Actor hitActor) { local ScriptedPawn avoidPawn; // We only care about HitWall as it pertains to level geometry if (hitActor != Level) return; // Are we walking? if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)) && bWalkAround && (AvoidWallTimer <= 0)) { // Are we turning? if (TurnDirection != TURNING_None) { AvoidWallTimer = 1.0; // About face TurnDirection = NextDirection; NextDirection = TURNING_None; bClearedObstacle = false; // Avoid pairing off avoidPawn = ScriptedPawn(ActorAvoiding); if (avoidPawn != None) { if ((avoidPawn.Acceleration != vect(0,0,0)) && (avoidPawn.Physics == PHYS_Walking) && (avoidPawn.TurnDirection != TURNING_None) && (avoidPawn.ActorAvoiding == self)) { if ((avoidPawn.TurnDirection == TURNING_Left) && (TurnDirection == TURNING_Right)) TurnDirection = TURNING_None; else if ((avoidPawn.TurnDirection == TURNING_Right) && (TurnDirection == TURNING_Left)) TurnDirection = TURNING_None; } } // Stopped turning? Shut down if (TurnDirection == TURNING_None) { ActorAvoiding = None; bAdvancedTactics = false; MoveTimer -= 4.0; ObstacleTimer = 0; } } } } // ---------------------------------------------------------------------- // AlterDestination() // ---------------------------------------------------------------------- function AlterDestination() { local Rotator dir; local int avoidYaw; local int destYaw; local int moveYaw; local int angle; local bool bPointInCylinder; local float dist1, dist2; local bool bAround; local vector tempVect; local ETurning oldTurnDir; oldTurnDir = TurnDirection; // Sanity check -- are we done walking around the actor? if (TurnDirection != TURNING_None) { if (!bWalkAround) TurnDirection = TURNING_None; else if (bClearedObstacle) TurnDirection = TURNING_None; else if (ActorAvoiding == None) TurnDirection = TURNING_None; else if (ActorAvoiding.bDeleteMe) TurnDirection = TURNING_None; else if (!IsPointInCylinder(ActorAvoiding, Location, CollisionRadius*2, CollisionHeight*2)) TurnDirection = TURNING_None; } // Are we still turning? if (TurnDirection != TURNING_None) { bAround = false; // Is our destination point inside the actor we're walking around? bPointInCylinder = IsPointInCylinder(ActorAvoiding, Destination, CollisionRadius-8, CollisionHeight-8); if (bPointInCylinder) { dist1 = VSize((Location - ActorAvoiding.Location)*vect(1,1,0)); dist2 = VSize((Location - Destination)*vect(1,1,0)); // Are we on the right side of the actor? if (dist1 > dist2) { // Just make a beeline, if possible tempVect = Destination - ActorAvoiding.Location; tempVect.Z = 0; tempVect = Normal(tempVect) * (ActorAvoiding.CollisionRadius + CollisionRadius); if (tempVect == vect(0,0,0)) Destination = Location; else { tempVect += ActorAvoiding.Location; tempVect.Z = Destination.Z; Destination = tempVect; } } else bAround = true; } else bAround = true; // We have a valid destination -- continue to walk around if (bAround) { // Determine the destination-self-obstacle angle dir = Rotator(ActorAvoiding.Location-Location); avoidYaw = dir.Yaw; dir = Rotator(Destination-Location); destYaw = dir.Yaw; if (TurnDirection == TURNING_Left) angle = (avoidYaw - destYaw) & 65535; else angle = (destYaw - avoidYaw) & 65535; if (angle < 0) angle += 65536; // If the angle is between 90 and 180 degrees, we've cleared the obstacle if (bPointInCylinder || (angle < 16384) || (angle > 32768)) // haven't cleared the actor yet { if (TurnDirection == TURNING_Left) moveYaw = avoidYaw - 16384; else moveYaw = avoidYaw + 16384; Destination = Location + Vector(rot(0,1,0)*moveYaw)*400; } else // cleared the actor -- move on TurnDirection = TURNING_None; } } if (TurnDirection == TURNING_None) { if (ObstacleTimer > 0) { TurnDirection = oldTurnDir; bClearedObstacle = true; } } else ObstacleTimer = 1.5; // Reset if done turning if (TurnDirection == TURNING_None) { NextDirection = TURNING_None; ActorAvoiding = None; bAdvancedTactics = false; ObstacleTimer = 0; bClearedObstacle = true; if (oldTurnDir != TURNING_None) MoveTimer -= 4.0; } } // ---------------------------------------------------------------------- // Frob() // ---------------------------------------------------------------------- function Frob(Actor Frobber, Inventory frobWith) { Super.Frob(Frobber, frobWith); // Check to see if the Frobber is the player. If so, then // check to see if we need to start a conversation. if (DeusExPlayer(Frobber) != None) { if (DeusExPlayer(Frobber).StartConversation(Self, IM_Frob)) { ConversationActor = Pawn(Frobber); return; } } } // ---------------------------------------------------------------------- // StopBlendAnims() // ---------------------------------------------------------------------- function StopBlendAnims() { AIAddViewRotation = rot(0, 0, 0); Super.StopBlendAnims(); PlayTurnHead(LOOK_Forward, 1.0, 1.0); } // ---------------------------------------------------------------------- // Falling() // ---------------------------------------------------------------------- singular function Falling() { if (bCanFly) { SetPhysics(PHYS_Flying); return; } // SetPhysics(PHYS_Falling); //note - done by default in physics if (health > 0) SetFall(); } // ---------------------------------------------------------------------- // SetFall() // ---------------------------------------------------------------------- function SetFall() { GotoState('FallingState'); } // ---------------------------------------------------------------------- // LongFall() // ---------------------------------------------------------------------- function LongFall() { SetFall(); GotoState('FallingState', 'LongFall'); } // ---------------------------------------------------------------------- // HearNoise() // ---------------------------------------------------------------------- function HearNoise(float Loudness, Actor NoiseMaker) { // Do nothing } // ---------------------------------------------------------------------- // SeePlayer() // ---------------------------------------------------------------------- function SeePlayer(Actor SeenPlayer) { // Do nothing } // ---------------------------------------------------------------------- // Trigger() // ---------------------------------------------------------------------- function Trigger( actor Other, pawn EventInstigator ) { // Do nothing } // ---------------------------------------------------------------------- // Reloading() // ---------------------------------------------------------------------- function Reloading(DeusExWeapon reloadWeapon, float reloadTime) { if (reloadWeapon == Weapon) ReloadTimer = reloadTime; } // ---------------------------------------------------------------------- // DoneReloading() // ---------------------------------------------------------------------- function DoneReloading(DeusExWeapon reloadWeapon) { if (reloadWeapon == Weapon) ReloadTimer = 0; } // ---------------------------------------------------------------------- // IsWeaponReloading() // ---------------------------------------------------------------------- function bool IsWeaponReloading() { return (ReloadTimer >= 0.3); } // ---------------------------------------------------------------------- // HandToHandAttack() // ---------------------------------------------------------------------- function HandToHandAttack() { local DeusExWeapon dxWeapon; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) dxWeapon.OwnerHandToHandAttack(); } // ---------------------------------------------------------------------- // Killed() // ---------------------------------------------------------------------- function Killed(pawn Killer, pawn Other, name damageType) { if ((Enemy == Other) && (Other != None) && !Other.bIsPlayer) Enemy = None; } // ---------------------------------------------------------------------- // Died() // ---------------------------------------------------------------------- function Died(pawn Killer, name damageType, vector HitLocation) { local DeusExPlayer player; local name flagName; // Set a flag when NPCs die so we can possibly check it later player = DeusExPlayer(GetPlayerPawn()); ExtinguishFire(); // set the instigator to be the killer Instigator = Killer; if (player != None) { // Abort any conversation we may have been having with the NPC if (bInConversation) player.AbortConversation(); // Abort any barks we may have been playing if (player.BarkManager != None) player.BarkManager.ScriptedPawnDied(Self); } Super.Died(Killer, damageType, HitLocation); // bStunned is set here if (player != None) { if (bImportant) { flagName = player.rootWindow.StringToName(BindName$"_Dead"); player.flagBase.SetBool(flagName, True); // make sure the flag never expires player.flagBase.SetExpiration(flagName, FLAG_Bool, 0); if (bStunned) { flagName = player.rootWindow.StringToName(BindName$"_Unconscious"); player.flagBase.SetBool(flagName, True); // make sure the flag never expires player.flagBase.SetExpiration(flagName, FLAG_Bool, 0); } } } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // STATES // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // state StartUp // // Initial state // ---------------------------------------------------------------------- auto state StartUp { function BeginState() { bInterruptState = true; bCanConverse = false; SetMovementPhysics(); if (Physics == PHYS_Walking) SetPhysics(PHYS_Falling); bStasis = False; SetDistress(false); BlockReactions(); ResetDestLoc(); } function EndState() { bCanConverse = true; bStasis = True; ResetReactions(); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); if (LastRendered() <= 1.0) { PlayWaiting(); InitializePawn(); FollowOrders(); } } Begin: InitializePawn(); Sleep(FRand()+0.2); WaitForLanding(); Start: FollowOrders(); } // ---------------------------------------------------------------------- // state Paralyzed // // Do nothing -- ignore all // (this state lets ViewModel work correctly) // ---------------------------------------------------------------------- state Paralyzed { ignores bump, frob, reacttoinjury; function BeginState() { StandUp(); BlockReactions(true); bCanConverse = False; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { ResetReactions(); bCanConverse = True; } Begin: Acceleration=vect(0,0,0); PlayAnimPivot('Still'); } // ---------------------------------------------------------------------- // state Idle // // Do nothing // ---------------------------------------------------------------------- state Idle { ignores bump, frob, reacttoinjury; function BeginState() { StandUp(); BlockReactions(true); bCanConverse = False; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { ResetReactions(); bCanConverse = True; } Begin: Acceleration=vect(0,0,0); DesiredRotation=Rotation; PlayAnimPivot('Still'); Idle: } // ---------------------------------------------------------------------- // state Conversation // // Just sit here until the conversation is over // ---------------------------------------------------------------------- state Conversation { function Tick(float deltaTime) { Global.Tick(deltaTime); LipSynch(deltaTime); // Keep turning towards the person we're speaking to if (ConversationActor != None) { if (bSitting) { if (SeatActor != None) LookAtActor(ConversationActor, true, true, true, 0, 0.5, SeatActor.Rotation.Yaw+49152, 5461); else LookAtActor(ConversationActor, false, true, true, 0, 0.5); } else LookAtActor(ConversationActor, true, true, true, 0, 0.5); } } function LoopHeadConvoAnim() { } function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate) { local DeusExPlayer dxPlayer; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) if (dxPlayer.ConPlay != None) if (dxPlayer.ConPlay.GetForcePlay()) { Global.SetOrders(orderName, newOrderTag, bImmediate); return; } ConvOrders = orderName; ConvOrderTag = newOrderTag; } function FollowOrders(optional bool bDefer) { local name tempConvOrders, tempConvOrderTag; // hack tempConvOrders = ConvOrders; tempConvOrderTag = ConvOrderTag; ResetConvOrders(); // must do this before calling SetOrders(), or recursion will result if (tempConvOrders != '') Global.SetOrders(tempConvOrders, tempConvOrderTag, true); else Global.FollowOrders(bDefer); } function BeginState() { local DeusExPlayer dxPlayer; local bool bBlock; ResetConvOrders(); EnableCheckDestLoc(false); bBlock = True; dxPlayer = DeusExPlayer(GetPlayerPawn()); if (dxPlayer != None) if (dxPlayer.ConPlay != None) if (dxPlayer.ConPlay.CanInterrupt()) bBlock = False; bInterruptState = True; if (bBlock) { bCanConverse = False; MakePawnIgnored(true); BlockReactions(true); } else { bCanConverse = True; MakePawnIgnored(true); BlockReactions(false); } // Check if the current state is "WaitingFor", "RunningTo" or "GoingTo", in which case // we want the orders to be 'Standing' after the conversation is over. UNLESS the // ScriptedPawn was going somewhere else (OrderTag != '') if (((Orders == 'WaitingFor') || (Orders == 'RunningTo') || (Orders == 'GoingTo')) && (OrderTag == '')) SetOrders('Standing'); bConversationEndedNormally = False; bInConversation = True; bStasis = False; SetDistress(false); SeekPawn = None; } function EndState() { local DeusExPlayer player; local bool bForcePlay; bForcePlay = false; player = DeusExPlayer(GetPlayerPawn()); if (player != None) if (player.conPlay != None) bForcePlay = player.conPlay.GetForcePlay(); bConvEndState = true; if (!bForcePlay && (bConversationEndedNormally != True)) player.AbortConversation(); bConvEndState = false; ResetConvOrders(); StopBlendAnims(); bInterruptState = true; bCanConverse = True; MakePawnIgnored(false); ResetReactions(); bStasis = True; ConversationActor = None; } Begin: Acceleration = vect(0,0,0); DesiredRotation.Pitch = 0; if (!bSitting && !bDancing) PlayWaiting(); // we are now idle } // ---------------------------------------------------------------------- // state FirstPersonConversation // // Just sit here until the conversation is over // ---------------------------------------------------------------------- state FirstPersonConversation { function Tick(float deltaTime) { Global.Tick(deltaTime); LipSynch(deltaTime); // Keep turning towards the person we're speaking to if (ConversationActor != None) { if (bSitting) { if (SeatActor != None) LookAtActor(ConversationActor, true, true, true, 0, 1.0, SeatActor.Rotation.Yaw+49152, 5461); else LookAtActor(ConversationActor, false, true, true, 0, 1.0); } else LookAtActor(ConversationActor, true, true, true, 0, 1.0); } } function LoopHeadConvoAnim() { } function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate) { ConvOrders = orderName; ConvOrderTag = newOrderTag; } function FollowOrders(optional bool bDefer) { local name tempConvOrders, tempConvOrderTag; // hack tempConvOrders = ConvOrders; tempConvOrderTag = ConvOrderTag; ResetConvOrders(); // must do this before calling SetOrders(), or recursion will result if (tempConvOrders != '') Global.SetOrders(tempConvOrders, tempConvOrderTag, true); else Global.FollowOrders(bDefer); } function BeginState() { local DeusExPlayer dxPlayer; local bool bBlock; ResetConvOrders(); EnableCheckDestLoc(false); dxPlayer = DeusExPlayer(GetPlayerPawn()); /* bBlock = True; if (dxPlayer != None) if (dxPlayer.ConPlay != None) if (dxPlayer.ConPlay.CanInterrupt()) bBlock = False; */ // 1st-person conversations will no longer block; // left old code in here in case people change their minds :) bBlock = false; bInterruptState = True; if (bBlock) { bCanConverse = False; MakePawnIgnored(true); BlockReactions(true); } else { bCanConverse = True; MakePawnIgnored(false); if ((dxPlayer != None) && (dxPlayer.conPlay != None) && dxPlayer.conPlay.con.IsSpeakingActor(dxPlayer)) SetReactions(false, false, false, false, true, false, false, true, false, false, true, true); else ResetReactions(); } bConversationEndedNormally = False; bInConversation = True; bStasis = False; SetDistress(false); SeekPawn = None; } function EndState() { local DeusExPlayer player; bConvEndState = true; if (bConversationEndedNormally != True) { player = DeusExPlayer(GetPlayerPawn()); player.AbortConversation(); } bConvEndState = false; ResetConvOrders(); StopBlendAnims(); bInterruptState = true; bCanConverse = True; MakePawnIgnored(false); ResetReactions(); bStasis = True; ConversationActor = None; } Begin: Acceleration = vect(0,0,0); DesiredRotation.Pitch = 0; if (!bSitting && !bDancing) PlayWaiting(); // we are now idle } // ---------------------------------------------------------------------- // EnterConversationState() // ---------------------------------------------------------------------- function EnterConversationState(bool bFirstPerson, optional bool bAvoidState) { // First check to see if we're already in a conversation state, // in which case we'll abort immediately if ((GetStateName() == 'Conversation') || (GetStateName() == 'FirstPersonConversation')) return; SetNextState(GetStateName(), 'Begin'); // If bAvoidState is set, then we don't want to jump into the conversaton state // for the ScriptedPawn, because bad things might happen otherwise. if (!bAvoidState) { if (bFirstPerson) GotoState('FirstPersonConversation'); else GotoState('Conversation'); } } // ---------------------------------------------------------------------- // state Standing // // Just kinda stand there and do nothing. // (similar to Wandering, except the pawn doesn't actually move) // ---------------------------------------------------------------------- state Standing { ignores EnemyNotVisible; function SetFall() { StartFalling('Standing', 'ContinueStand'); } function AnimEnd() { PlayWaiting(); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Tick(float deltaSeconds) { animTimer[1] += deltaSeconds; Global.Tick(deltaSeconds); } function BeginState() { StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bStasis = True; StopBlendAnims(); } Begin: WaitForLanding(); if (!bUseHome) Goto('StartStand'); MoveToBase: if (!IsPointInCylinder(self, HomeLoc, 16-CollisionRadius)) { EnableCheckDestLoc(true); while (true) { if (PointReachable(HomeLoc)) { if (ShouldPlayWalk(HomeLoc)) PlayWalking(); MoveTo(HomeLoc, GetWalkingSpeed()); CheckDestLoc(HomeLoc); break; } else { MoveTarget = FindPathTo(HomeLoc); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } EnableCheckDestLoc(false); } TurnTo(Location+HomeRot); StartStand: Acceleration=vect(0,0,0); Goto('Stand'); ContinueFromDoor: Goto('MoveToBase'); Stand: ContinueStand: // nil bStasis = True; PlayWaiting(); if (!bPlayIdle) Goto('DoNothing'); Sleep(FRand()*14+8); Fidget: if (FRand() < 0.5) { PlayIdle(); FinishAnim(); } else { if (FRand() > 0.5) { PlayTurnHead(LOOK_Up, 1.0, 1.0); Sleep(2.0); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); } else if (FRand() > 0.5) { PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.5); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.9); PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.2); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); } else { PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.5); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.9); PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.2); PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); } } if (FRand() < 0.3) PlayIdleSound(); Goto('Stand'); DoNothing: // nil } // ---------------------------------------------------------------------- // state Dancing // // Dance in place. // (Most of this code was ripped from Standing) // ---------------------------------------------------------------------- state Dancing { ignores EnemyNotVisible; function SetFall() { StartFalling('Dancing', 'ContinueDance'); } function AnimEnd() { PlayDancing(); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function BeginState() { if (bSitting && !bDancing) StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bStasis = True; StopBlendAnims(); } Begin: WaitForLanding(); if (bDancing) { if (bUseHome) TurnTo(Location+HomeRot); Goto('StartDance'); } if (!bUseHome) Goto('StartDance'); MoveToBase: if (!IsPointInCylinder(self, HomeLoc, 16-CollisionRadius)) { EnableCheckDestLoc(true); while (true) { if (PointReachable(HomeLoc)) { if (ShouldPlayWalk(HomeLoc)) PlayWalking(); MoveTo(HomeLoc, GetWalkingSpeed()); CheckDestLoc(HomeLoc); break; } else { MoveTarget = FindPathTo(HomeLoc); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } EnableCheckDestLoc(false); } TurnTo(Location+HomeRot); StartDance: Acceleration=vect(0,0,0); Goto('Dance'); ContinueFromDoor: Goto('MoveToBase'); Dance: ContinueDance: // nil bDancing = True; PlayDancing(); bStasis = True; if (!bHokeyPokey) Goto('DoNothing'); Spin: Sleep(FRand()*5+5); useRot = DesiredRotation; if (FRand() > 0.5) { TurnTo(Location+1000*vector(useRot+rot(0,16384,0))); TurnTo(Location+1000*vector(useRot+rot(0,32768,0))); TurnTo(Location+1000*vector(useRot+rot(0,49152,0))); } else { TurnTo(Location+1000*vector(useRot+rot(0,49152,0))); TurnTo(Location+1000*vector(useRot+rot(0,32768,0))); TurnTo(Location+1000*vector(useRot+rot(0,16384,0))); } TurnTo(Location+1000*vector(useRot)); Goto('Spin'); DoNothing: // nil } // ---------------------------------------------------------------------- // state Sitting // // Just kinda sit there and do nothing. // (doubles as a shitting state) // ---------------------------------------------------------------------- state Sitting { ignores EnemyNotVisible; function SetFall() { StartFalling('Sitting', 'ContinueSit'); } function AnimEnd() { PlayWaiting(); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; if (!bAcceptBump) NextDirection = TURNING_None; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool HandleTurn(Actor Other) { if (Other == SeatActor) return true; else return Global.HandleTurn(Other); } function Bump(actor bumper) { // If we hit our chair, move to the right place if ((bumper == SeatActor) && bAcceptBump) { bAcceptBump = false; GotoState('Sitting', 'CircleToFront'); } // Handle conversations, if need be else Global.Bump(bumper); } function Tick(float deltaSeconds) { local vector endPos; local vector newPos; local float delta; Global.Tick(deltaSeconds); if (bSitInterpolation && (SeatActor != None)) { endPos = SitPosition(SeatActor, SeatSlot); if ((deltaSeconds < remainingSitTime) && (remainingSitTime > 0)) { delta = deltaSeconds/remainingSitTime; newPos = (endPos-Location)*delta + Location; remainingSitTime -= deltaSeconds; } else { remainingSitTime = 0; bSitInterpolation = false; newPos = endPos; Acceleration = vect(0,0,0); Velocity = vect(0,0,0); SetBase(SeatActor); bSitting = true; } SetLocation(newPos); DesiredRotation = SeatActor.Rotation+Rot(0, -16384, 0); } } function Vector SitPosition(Seat seatActor, int slot) { local float newAssHeight; newAssHeight = GetDefaultCollisionHeight() + BaseAssHeight; newAssHeight = -(CollisionHeight - newAssHeight); return ((seatActor.sitPoint[slot]>>seatActor.Rotation)+seatActor.Location+(vect(0,0,-1)*newAssHeight)); } function vector GetDestinationPosition(Seat seatActor, optional float extraDist) { local Rotator seatRot; local Vector destPos; if (seatActor == None) return (Location); seatRot = seatActor.Rotation + Rot(0, -16384, 0); seatRot.Pitch = 0; destPos = seatActor.Location; destPos += vect(0,0,1)*(CollisionHeight-seatActor.CollisionHeight); destPos += Vector(seatRot)*(seatActor.CollisionRadius+CollisionRadius+extraDist); return (destPos); } function bool IsIntersectingSeat() { local bool bIntersect; local vector testVector; bIntersect = false; if (SeatActor != None) bIntersect = IsOverlapping(SeatActor); return (bIntersect); } function int FindBestSlot(Seat seatActor, out float slotDist) { local int bestSlot; local float dist; local float bestDist; local int i; bestSlot = -1; bestDist = 100; if (!seatActor.Region.Zone.bWaterZone) { for (i=0; i<seatActor.numSitPoints; i++) { if (seatActor.sittingActor[i] == None) { dist = VSize(SitPosition(seatActor, i) - Location); if ((bestSlot < 0) || (bestDist > dist)) { bestDist = dist; bestSlot = i; } } } } slotDist = bestDist; return (bestSlot); } function FindBestSeat() { local Seat curSeat; local Seat bestSeat; local float curDist; local float bestDist; local int curSlot; local int bestSlot; local bool bTry; if (bUseFirstSeatOnly && bSeatHackUsed) { bestSeat = SeatHack; // use the seat hack bestSlot = -1; if (!IsSeatValid(bestSeat)) bestSeat = None; else { if (GetNextWaypoint(bestSeat) == None) bestSeat = None; else { bestSlot = FindBestSlot(bestSeat, curDist); if (bestSlot < 0) bestSeat = None; } } } else { bestSeat = Seat(OrderActor); // try the ordered seat first if (bestSeat != None) { if (!IsSeatValid(OrderActor)) bestSeat = None; else { if (GetNextWaypoint(bestSeat) == None) bestSeat = None; else { bestSlot = FindBestSlot(bestSeat, curDist); if (bestSlot < 0) bestSeat = None; } } } if (bestSeat == None) { bestDist = 10001; bestSlot = -1; foreach RadiusActors(Class'Seat', curSeat, 10000) { if (IsSeatValid(curSeat)) { curSlot = FindBestSlot(curSeat, curDist); if (curSlot >= 0) { if (bestDist > curDist) { if (GetNextWaypoint(curSeat) != None) { bestDist = curDist; bestSeat = curSeat; bestSlot = curSlot; } } } } } } } if (bestSeat != None) { bestSeat.sittingActor[bestSlot] = self; SeatLocation = bestSeat.Location; bSeatLocationValid = true; } else bSeatLocationValid = false; if (bUseFirstSeatOnly && !bSeatHackUsed) { SeatHack = bestSeat; bSeatHackUsed = true; } SeatActor = bestSeat; SeatSlot = bestSlot; } function FollowSeatFallbackOrders() { FindBestSeat(); if (IsSeatValid(SeatActor)) GotoState('Sitting', 'Begin'); else GotoState('Wandering'); } function BeginState() { SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; bAcceptBump = True; if (SeatActor == None) FindBestSeat(); bSitInterpolation = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); if (!bSitting) StandUp(); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bSitInterpolation = false; bStasis = True; } Begin: WaitForLanding(); if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); if (!bSitting) WaitForLanding(); else { TurnTo(Vector(SeatActor.Rotation+Rot(0, -16384, 0))*100+Location); Goto('ContinueSitting'); } MoveToSeat: if (IsIntersectingSeat()) Goto('MoveToPosition'); bAcceptBump = true; while (true) { if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); destLoc = GetDestinationPosition(SeatActor); if (PointReachable(destLoc)) { if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); CheckDestLoc(destLoc); if (IsPointInCylinder(self, GetDestinationPosition(SeatActor), 16, 16)) { bAcceptBump = false; Goto('MoveToPosition'); break; } } else { MoveTarget = GetNextWaypoint(SeatActor); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } CircleToFront: bAcceptBump = false; if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); if (ShouldPlayWalk(GetDestinationPosition(SeatActor, 16))) PlayWalking(); MoveTo(GetDestinationPosition(SeatActor, 16), GetWalkingSpeed()); MoveToPosition: if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); bSitting = true; EnableCollision(false); Acceleration=vect(0,0,0); Sit: Acceleration=vect(0,0,0); Velocity=vect(0,0,0); if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); remainingSitTime = 0.8; PlaySittingDown(); SetBasedPawnSize(CollisionRadius, GetSitHeight()); SetPhysics(PHYS_Flying); StopStanding(); bSitInterpolation = true; while (bSitInterpolation) Sleep(0); FinishAnim(); Goto('ContinueSitting'); ContinueFromDoor: Goto('MoveToSeat'); ContinueSitting: if (!IsSeatValid(SeatActor)) FollowSeatFallbackOrders(); SetBasedPawnSize(CollisionRadius, GetSitHeight()); SetCollision(Default.bCollideActors, Default.bBlockActors, Default.bBlockPlayers); PlaySitting(); bStasis = True; // nil } // ---------------------------------------------------------------------- // state HandlingEnemy // // Fight-or-flight state // ---------------------------------------------------------------------- state HandlingEnemy { function BeginState() { if (Enemy == None) GotoState('Seeking'); else if (RaiseAlarm == RAISEALARM_BeforeAttacking) GotoState('Alerting'); else GotoState('Attacking'); } Begin: } // ---------------------------------------------------------------------- // state Wandering // // Meander from place to place, occasionally gazing into the middle // distance. // ---------------------------------------------------------------------- state Wandering { ignores EnemyNotVisible; function SetFall() { StartFalling('Wandering', 'ContinueWander'); } function Bump(actor bumper) { if (bAcceptBump) { // If we get bumped by another actor while we wait, start wandering again bAcceptBump = False; Disable('AnimEnd'); GotoState('Wandering', 'Wander'); } // Handle conversations, if need be Global.Bump(bumper); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool GoHome() { if (bUseHome && !IsNearHome(Location)) { destLoc = HomeLoc; destPoint = None; if (PointReachable(destLoc)) return true; else { MoveTarget = FindPathTo(destLoc); if (MoveTarget != None) return true; else return false; } } else return false; } function PickDestination() { local WanderCandidates candidates[5]; local int candidateCount; local int maxCandidates; local int maxLastPoints; local WanderPoint curPoint; local Actor wayPoint; local int i; local int openSlot; local float maxDist; local float dist; local float angle; local float magnitude; local int iterations; local bool bSuccess; local Rotator rot; maxCandidates = 4; // must be <= size of candidates[] array maxLastPoints = 2; // must be <= size of lastPoints[] array for (i=0; i<maxCandidates; i++) candidates[i].dist = 100000; candidateCount = 0; // A certain percentage of the time, we want to angle off to a random direction... if ((RandomWandering < 1) && (FRand() > RandomWandering)) { // Fill the candidate table foreach RadiusActors(Class'WanderPoint', curPoint, 3000*wanderlust+1000) // 1000-4000 { // Make sure we haven't been here recently for (i=0; i<maxLastPoints; i++) { if (lastPoints[i] == curPoint) break; } if (i >= maxLastPoints) { // Can we get there from here? wayPoint = GetNextWaypoint(curPoint); if ((wayPoint != None) && !IsNearHome(curPoint.Location)) wayPoint = None; // Yep if (wayPoint != None) { // Find an empty slot for this candidate openSlot = -1; dist = VSize(curPoint.location - location); maxDist = dist; // This candidate will only replace more distant candidates... for (i=0; i<maxCandidates; i++) { if (maxDist < candidates[i].dist) { maxDist = candidates[i].dist; openSlot = i; } } // Put the candidate in the (unsorted) list if (openSlot >= 0) { candidates[openSlot].point = curPoint; candidates[openSlot].waypoint = wayPoint; candidates[openSlot].dist = dist; if (candidateCount < maxCandidates) candidateCount++; } } } } } // Shift our list of recently visited points for (i=maxLastPoints-1; i>0; i--) lastPoints[i] = lastPoints[i-1]; lastPoints[0] = None; // Do we have a list of candidates? if (candidateCount > 0) { // Pick a candidate at random i = Rand(candidateCount); curPoint = candidates[i].point; wayPoint = candidates[i].waypoint; lastPoints[0] = curPoint; MoveTarget = wayPoint; destPoint = curPoint; } // No candidates -- find a random place to go else { MoveTarget = None; destPoint = None; iterations = 6; // try up to 6 different directions while (iterations > 0) { // How far will we go? magnitude = (wanderlust*400+200) * (FRand()*0.2+0.9); // 200-600, +/-10% // Choose our destination, based on whether we have a home base if (!bUseHome) bSuccess = AIPickRandomDestination(100, magnitude, 0, 0, 0, 0, 1, FRand()*0.4+0.35, destLoc); else { if (magnitude > HomeExtent) magnitude = HomeExtent*(FRand()*0.2+0.9); rot = Rotator(HomeLoc-Location); bSuccess = AIPickRandomDestination(50, magnitude, rot.Yaw, 0.25, rot.Pitch, 0.25, 1, FRand()*0.4+0.35, destLoc); } // Success? Break out of the iteration loop if (bSuccess) if (IsNearHome(destLoc)) break; // We failed -- try again iterations--; } // If we got a destination, go there if (iterations <= 0) destLoc = Location; } } function AnimEnd() { PlayWaiting(); } function BeginState() { StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); bCanJump = false; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { local int i; bAcceptBump = True; EnableCheckDestLoc(false); // Clear out our list of last visited points for (i=0; i<ArrayCount(lastPoints); i++) lastPoints[i] = None; if (JumpZ > 0) bCanJump = true; } Begin: destPoint = None; GoHome: bAcceptBump = false; WaitForLanding(); if (!GoHome()) Goto('WanderInternal'); MoveHome: EnableCheckDestLoc(true); while (true) { if (PointReachable(destLoc)) { if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); CheckDestLoc(destLoc); break; } else { MoveTarget = FindPathTo(destLoc); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } else break; } } EnableCheckDestLoc(false); Goto('Pausing'); Wander: WaitForLanding(); WanderInternal: PickDestination(); Moving: // Move from pathnode to pathnode until we get where we're going // (ooooold code -- no longer used) if (destPoint != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); while ((MoveTarget != None) && (MoveTarget != destPoint)) { MoveTarget = FindPathToward(destPoint); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); } } } else if (destLoc != Location) { if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); } else Sleep(0.5); Pausing: Acceleration = vect(0, 0, 0); // Turn in the direction dictated by the WanderPoint, if there is one sleepTime = 6.0; if (WanderPoint(destPoint) != None) { if (WanderPoint(destPoint).gazeItem != None) { TurnToward(WanderPoint(destPoint).gazeItem); sleepTime = WanderPoint(destPoint).gazeDuration; } else if (WanderPoint(destPoint).gazeDirection != vect(0, 0, 0)) TurnTo(Location + WanderPoint(destPoint).gazeDirection); } Enable('AnimEnd'); TweenToWaiting(0.2); bAcceptBump = True; PlayScanningSound(); sleepTime *= (-0.9*restlessness) + 1; Sleep(sleepTime); Disable('AnimEnd'); bAcceptBump = False; FinishAnim(); Goto('Wander'); ContinueWander: ContinueFromDoor: FinishAnim(); PlayWalking(); Goto('Wander'); } // ---------------------------------------------------------------------- // state Leaving // // Just like Patrolling, but make the pawn transient. // ---------------------------------------------------------------------- State Leaving { function BeginState() { bTransient = True; // this pawn will be destroyed when it gets out of range bDisappear = True; GotoState('Patrolling'); } Begin: // shouldn't ever reach this point } // ---------------------------------------------------------------------- // state Patrolling // // Move from point to point in a predescribed pattern. // ---------------------------------------------------------------------- State Patrolling { function SetFall() { StartFalling('Patrolling', 'ContinuePatrol'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function PatrolPoint PickStartPoint() { local NavigationPoint nav; local PatrolPoint curNav; local float curDist; local PatrolPoint closestNav; local float closestDist; nav = Level.NavigationPointList; while (nav != None) { nav.visitedWeight = 0; nav = nav.nextNavigationPoint; } closestNav = None; closestDist = 100000; nav = Level.NavigationPointList; while (nav != None) { curNav = PatrolPoint(nav); if ((curNav != None) && (curNav.Tag == OrderTag)) { while (curNav != None) { if (curNav.visitedWeight != 0) // been here before break; curDist = VSize(Location - curNav.Location); if ((closestNav == None) || (closestDist > curDist)) { closestNav = curNav; closestDist = curDist; } curNav.visitedWeight = 1; curNav = curNav.NextPatrolPoint; } } nav = nav.nextNavigationPoint; } return (closestNav); } function PickDestination() { if (PatrolPoint(destPoint) != None) destPoint = PatrolPoint(destPoint).NextPatrolPoint; else destPoint = PickStartPoint(); if (destPoint == None) // can't go anywhere... GotoState('Standing'); } function BeginState() { StandUp(); SetEnemy(None, EnemyLastSeen, true); Disable('AnimEnd'); SetupWeapon(false); SetDistress(false); bStasis = false; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); Enable('AnimEnd'); bStasis = true; } Begin: destPoint = None; Patrol: //Disable('Bump'); WaitForLanding(); PickDestination(); Moving: // Move from pathnode to pathnode until we get where we're going if (destPoint != None) { if (!IsPointInCylinder(self, destPoint.Location, 16-CollisionRadius)) { EnableCheckDestLoc(true); MoveTarget = FindPathToward(destPoint); while (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); if (MoveTarget == destPoint) break; MoveTarget = FindPathToward(destPoint); } EnableCheckDestLoc(false); } } else Goto('Patrol'); Pausing: if (!bAlwaysPatrol) bStasis = true; Acceleration = vect(0, 0, 0); // Turn in the direction dictated by the WanderPoint, or a random direction if (PatrolPoint(destPoint) != None) { if ((PatrolPoint(destPoint).pausetime > 0) || (PatrolPoint(destPoint).NextPatrolPoint == None)) { if (ShouldPlayTurn(Location + PatrolPoint(destPoint).lookdir)) PlayTurning(); TurnTo(Location + PatrolPoint(destPoint).lookdir); Enable('AnimEnd'); TweenToWaiting(0.2); PlayScanningSound(); //Enable('Bump'); sleepTime = PatrolPoint(destPoint).pausetime * ((-0.9*restlessness) + 1); Sleep(sleepTime); Disable('AnimEnd'); //Disable('Bump'); FinishAnim(); } } Goto('Patrol'); ContinuePatrol: ContinueFromDoor: FinishAnim(); PlayWalking(); Goto('Moving'); } // ---------------------------------------------------------------------- // state Seeking // // Look for enemies in the area // ---------------------------------------------------------------------- State Seeking { function SetFall() { StartFalling('Seeking', 'ContinueSeek'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool GetNextLocation(out vector nextLoc) { local float dist; local rotator rotation; local bool bDone; local float seekDistance; local Actor hitActor; local vector HitLocation, HitNormal; local vector diffVect; local bool bLOS; if (bSeekLocation) { if (SeekType == SEEKTYPE_Guess) seekDistance = (200+FClamp(GroundSpeed*EnemyLastSeen*0.5, 0, 1000)); else seekDistance = 300; } else seekDistance = 60; dist = VSize(Location-destLoc); bDone = false; bLOS = false; if (dist < seekDistance) { bLOS = true; foreach TraceVisibleActors(Class'Actor', hitActor, hitLocation, hitNormal, destLoc, Location+vect(0,0,1)*BaseEyeHeight) { if (hitActor != self) { if (hitActor == Level) bLOS = false; else if (IsPointInCylinder(hitActor, destLoc, 16, 16)) break; else if (hitActor.bBlockSight && !hitActor.bHidden) bLOS = false; } if (!bLOS) break; } } if (!bLOS) { if (PointReachable(destLoc)) { rotation = Rotator(destLoc - Location); if (seekDistance == 0) nextLoc = destLoc; else if (!AIDirectionReachable(destLoc, rotation.Yaw, rotation.Pitch, 0, seekDistance, nextLoc)) bDone = true; if (!bDone && bDefendHome && !IsNearHome(nextLoc)) bDone = true; if (!bDone) // hack, because Unreal's movement code SUCKS { diffVect = nextLoc - Location; if (Physics == PHYS_Walking) diffVect *= vect(1,1,0); if (VSize(diffVect) < 20) bDone = true; else if (IsPointInCylinder(self, nextLoc, 10, 10)) bDone = true; } } else { MoveTarget = FindPathTo(destLoc); if (MoveTarget == None) bDone = true; else if (bDefendHome && !IsNearHome(MoveTarget.Location)) bDone = true; else nextLoc = MoveTarget.Location; } } else bDone = true; return (!bDone); } function bool PickDestination() { local bool bValid; bValid = false; if (/*(EnemyLastSeen <= 25.0) &&*/ (SeekLevel > 0)) { if (bSeekLocation) { bValid = true; destLoc = LastSeenPos; } else { bValid = AIPickRandomDestination(130, 320, 0, 0, 0, 0, 2, 1.0, destLoc); //CyberP: param 2 was 250 if (!bValid) { bValid = true; destLoc = Location + VRand()*50; } else destLoc += vect(0,0,1)*BaseEyeHeight; } } return (bValid); } function NavigationPoint GetOvershootDestination(float randomness, optional float focus) { local NavigationPoint navPoint, bestPoint; local float distance; local float score, bestScore; local int yaw; local rotator rot; local float yawCutoff; if (focus <= 0) focus = 0.6; yawCutoff = int(32768*focus); bestPoint = None; bestScore = 0; foreach ReachablePathnodes(Class'NavigationPoint', navPoint, None, distance) { if (distance < 1) distance = 1; rot = Rotator(navPoint.Location-Location); yaw = rot.Yaw + (16384*randomness); yaw = (yaw-Rotation.Yaw) & 0xFFFF; if (yaw > 32767) yaw -= 65536; yaw = abs(yaw); if (yaw <= yawCutoff) { score = yaw/distance; if ((bestPoint == None) || (score < bestScore)) { bestPoint = navPoint; bestScore = score; } } } return bestPoint; } function Tick(float deltaSeconds) { animTimer[1] += deltaSeconds; Global.Tick(deltaSeconds); UpdateActorVisibility(Enemy, deltaSeconds, 0.5, true); //CyberP: was 1.0 } function HandleLoudNoise(Name event, EAIEventState state, XAIParams params) { local Actor bestActor; local Pawn instigator; local float locDiff; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if ((bestActor != None) && (EnemyLastSeen > 2.0)) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { if (IsValidEnemy(instigator)) { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); destLoc = LastSeenPos; locDiff = Abs(VSize(instigator.Location - Location)); if (bInterruptSeek && locDiff > 416) //CyberP: only turn around when close range GotoState('Seeking', 'GoToLocation'); } } } } } //GMDX: HandleGenericNoise test function HandleGenericNoise(Name event, EAIEventState state, XAIParams params) { local Actor bestActor; local Pawn instigator; if (state == EAISTATE_Begin || state == EAISTATE_Pulse) { bestActor = params.bestActor; if ((bestActor != None) && (EnemyLastSeen > 2.0)) { instigator = Pawn(bestActor); if (instigator == None) instigator = bestActor.Instigator; if (instigator != None) { SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound); destLoc = LastSeenPos; if (bInterruptSeek) GotoState('Seeking', 'GoToLocation'); } } } } function HandleSighting(Pawn pawnSighted) { if ((EnemyLastSeen > 2.0) && IsValidEnemy(pawnSighted)) { SetSeekLocation(pawnSighted, pawnSighted.Location, SEEKTYPE_Sight); destLoc = LastSeenPos; if (bInterruptSeek) GotoState('Seeking', 'GoToLocation'); } } function checkFlareThrow() { local DeusExPlayer playa; local float dista, visi; local int casted; bCanFlare = False; playa = DeusExPlayer(GetPlayerPawn()); if (playa != none && playa.CombatDifficulty > 0.5) { if (FastTrace(destLoc,Location)) { dista = Abs(VSize(playa.Location - Location)); if (dista < 800 && (Abs(Location.Z-playa.Location.Z) < 160)) //CyberP: make sure we are close and without much height diff. { visi = Playa.AIVisibility(false); casted = (int(visi*100)); if (casted < 15) bCanFlare = True; } } } } function flareThrow() { local flare flared; local vector flareOffset; flareOffset = Location; flareOffset.Z += 50; flareOffset.X += CollisionRadius; flared = Spawn(class'Flare',self,,flareOffset,ViewRotation); if (flared != None) { flared.LightFlare(); flared.Velocity = Vector(Rotation)*1000; flared.Velocity.Z += 100; } } function BeginState() { StandUp(); Disable('AnimEnd'); destLoc = LastSeenPos; SetReactions(true, true, false, true, true, true, true, true, true, false, true, true); bCanConverse = False; bStasis = False; SetupWeapon(true); SetDistress(false); bInterruptState=True; //CyberP: commented out for now //set back EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); Enable('AnimEnd'); ResetReactions(); bCanConverse = True; bStasis = True; StopBlendAnims(); SeekLevel = 0; } Begin: WaitForLanding(); PlayWaiting(); if ((Weapon != None) && bKeepWeaponDrawn && (Weapon.CockingSound != None) && !bSeekPostCombat) { if(DeusExPlayer(GetPlayerPawn()).DrugEffectTimer < 0) PlaySound(Weapon.CockingSound, SLOT_None,,, 1024, 0.5); else PlaySound(Weapon.CockingSound, SLOT_None,,, 1024); } Acceleration = vect(0,0,0); if (!PickDestination()) Goto('DoneSeek'); GoToLocation: bInterruptSeek = true; Acceleration = vect(0,0,0); if ((DeusExWeapon(Weapon) != None) && DeusExWeapon(Weapon).CanReload() && !Weapon.IsInState('Reload')) DeusExWeapon(Weapon).ReloadAmmo(); if (bSeekPostCombat) PlayPostAttackSearchingSound(); else if (SeekType == SEEKTYPE_Sound) { PlayPreAttackSearchingSound(); disturbanceCount++; if (FRand() < 0.4 && SeekLevel >= 3) //CyberP: sometimes just turn to the source of the sound instead of running around { SeekLevel = 0; GoTo('TurnToLocation'); } } else if (SeekType == SEEKTYPE_Sight) { if (ReactionLevel > 0.5) PlayPreAttackSightingSound(); disturbanceCount++; } else if ((SeekType == SEEKTYPE_Carcass) && bSeekLocation) PlayCarcassSound(); StopBlendAnims(); if ((SeekType == SEEKTYPE_Sight) && bSeekLocation) Goto('TurnToLocation'); EnableCheckDestLoc(true); if (SeekType == SEEKTYPE_Sound && FRand() < 0.1 && SeekLevel >=3) { while (GetNextLocation(useLoc)) { if (ShouldPlayWalk(useLoc)) PlayWalking(); MoveTo(useLoc, GetWalkingSpeed()); CheckDestLoc(useLoc); } } else { while (GetNextLocation(useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CheckDestLoc(useLoc); } } EnableCheckDestLoc(false); if ((SeekType == SEEKTYPE_Guess) && bSeekLocation) { MoveTarget = GetOvershootDestination(0.5); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); } if (AIPickRandomDestination(CollisionRadius*2, 200+FRand()*200, Rotation.Yaw, 0.75, Rotation.Pitch, 0.75, 2, 0.4, useLoc)) { if (ShouldPlayWalk(useLoc)) { PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); } } } TurnToLocation: Acceleration = vect(0,0,0); PlayTurning(); if ((SeekType == SEEKTYPE_Guess) && bSeekLocation) destLoc = Location + Vector(Rotation+(rot(0,1,0)*(Rand(16384)-8192)))*1000; if (bCanTurnHead) { Sleep(0); // needed to turn head LookAtVector(destLoc, true, false, true); TurnTo(Vector(DesiredRotation)*1000+Location); } else TurnTo(destLoc); bSeekLocation = false; bInterruptSeek = True; //CyberP: was false PlayWaiting(); if (SeekType == SEEKTYPE_Sound && SeekLevel == 0) { Sleep(FRand()*2+3); GoTo('LookAroundAgain'); } /*if (disturbanceCount > 16 && disturbanceCount < 19) { if (RaiseAlarm != RAISEALARM_Never) { RaiseAlarm = RAISEALARM_BeforeAttacking; //CyberP: pawns that witness too many disturbances go for alarm GotoState('Alerting'); } }*/ if (SeekType == SEEKTYPE_Sight) //CyberP: if type sight, sleep for FRand()+1. { if (IsA('Doberman') && FRand() < 0.5) PlaySound(sound'DogAttack2',SLOT_None,,,1024); Sleep(FRand()+1.0); if (FRand() < 0.4) //CyberP: begin { SeekType = SEEKTYPE_Guess; GoTo('GoToLocation'); } //CyberP: end. This makes pawns sometimes seek the location else { if ((IsA('Terrorist') || IsA('MJ12Troop') || IsA('UNATCOTroop')) && !bHasThrownFlare) //CyberP: flare throw checkFlareThrow(); if (bCanFlare) { FinishAnim(); PlayAnimPivot('Attack',,0.1); Sleep(0.3); flareThrow(); bHasThrownFlare = True; bCanFlare = False; FinishAnim(); PlayWaiting(); Sleep(FRand()+0.1); if (FRand() < 0.5) //CyberP: sometimes go over to where you threw the flare too { SeekType = SEEKTYPE_Guess; GoTo('GoToLocation'); } } } } else Sleep(FRand()+0.2); //CyberP: else we don't want much sleeping on the job. :) FindAnotherPlaceAgainAgain: //CyberP: New label for another run around if (FRand() < 0.85) //CyberP: sometimes seek level isn't decremented SeekLevel--; if (PickDestination()) Goto('GoToLocation'); LookAround: //CyberP: look around now takes less time to execute if (bCanTurnHead) { if (FRand() < 0.5) { if (!bSeekLocation) { PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.0); } } else { if (!bSeekLocation) { PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.0); } } PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); StopBlendAnims(); } else { if (!bSeekLocation) Sleep(0.6); //CyberP: was 1.0 } if (SeekType == SEEKTYPE_Carcass) { if (RaiseAlarm != RAISEALARM_Never) { RaiseAlarm = RAISEALARM_BeforeAttacking; //CyberP: pawns that spot carcasses go for alarm GotoState('Alerting'); } } FindAnotherPlace: //CyberP: New label for another run around SeekLevel--; if (PickDestination()) Goto('GoToLocation'); LookAroundAgain: if (bCanTurnHead) { if (FRand() < 0.3) { if (!bSeekLocation) { PlayTurnHead(LOOK_Left, 1.0, 1.0); Sleep(1.0); } } else { if (!bSeekLocation) { PlayTurnHead(LOOK_Right, 1.0, 1.0); Sleep(1.0); } } PlayTurnHead(LOOK_Forward, 1.0, 1.0); Sleep(0.5); StopBlendAnims(); } else { if (!bSeekLocation) Sleep(0.6); //CyberP: was 1.0 } FindAnotherPlaceAgain: //CyberP: New label for another run around SeekLevel--; if (PickDestination()) Goto('GoToLocation'); DoneSeek: if (bSeekPostCombat) PlayTargetLostSound(); else if (RaiseAlarm != RAISEALARM_BeforeAttacking) //CyberP: bad hack so they don't say anything after alarm trigger PlaySearchGiveUpSound(); bSeekPostCombat = false; SeekPawn = None; if (Orders != 'Seeking') FollowOrders(); else GotoState('Wandering'); ContinueSeek: ContinueFromDoor: FinishAnim(); if (FRand() < 0.7) //CyberP: continue seeking post-opening a door, you dumbasses. { SeekType = SEEKTYPE_Guess; Goto('GoToLocation'); } else Goto('FindAnotherPlace'); } // ---------------------------------------------------------------------- // state Fleeing // // Run like a bat outta hell away from an actor. // ---------------------------------------------------------------------- State Fleeing { function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local Name currentState; local Pawn oldEnemy; local name newLabel; local bool bHateThisInjury; local bool bFearThisInjury; local bool bAttack; if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury)) { currentState = GetStateName(); bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury); bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury); if (bHateThisInjury) IncreaseAgitation(instigatedBy); if (bFearThisInjury) IncreaseFear(instigatedBy, 2.0); oldEnemy = Enemy; bAttack = false; if (SetEnemy(instigatedBy)) { if (!ShouldFlee()) { SwitchToBestWeapon(); if (Weapon != None) bAttack = true; } } else SetEnemy(instigatedBy, , true); if (bAttack) { SetDistressTimer(); SetNextState('HandlingEnemy'); } else { SetDistressTimer(); if (oldEnemy != Enemy) newLabel = 'Begin'; else newLabel = 'ContinueFlee'; SetNextState('Fleeing', newLabel); } GotoDisabledState(damageType, hitPos); } } function SetFall() { StartFalling('Fleeing', 'ContinueFlee'); } function FinishFleeing() { if (bLeaveAfterFleeing) GotoState('Wandering'); else FollowOrders(); } function bool InSeat(out vector newLoc) // hack { local Seat curSeat; local bool bSeat; bSeat = false; foreach RadiusActors(Class'Seat', curSeat, 200) { if (IsOverlapping(curSeat)) { bSeat = true; newLoc = curSeat.Location + vector(curSeat.Rotation+Rot(0, -16384, 0))*(CollisionRadius+curSeat.CollisionRadius+20); break; } } return (bSeat); } function Tick(float deltaSeconds) { UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false); if (IsValidEnemy(Enemy)) { if (EnemyLastSeen > FearSustainTime) FinishFleeing(); } else if (!IsValidEnemy(Enemy, false)) FinishFleeing(); else if (!IsFearful()) FinishFleeing(); Global.Tick(deltaSeconds); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function PickDestination() { local HidePoint hidePoint; local Actor waypoint; local float dist; local float score; local Vector vector1, vector2; local Rotator rotator1; local float tmpDist; local float bestDist; local float bestScore; local FleeCandidates candidates[5]; local int candidateCount; local int maxCandidates; local float maxDist; local int openSlot; local float maxScore; local int i; local bool bReplace; local float angle; local float magnitude; local int iterations; local NearbyProjectileList projList; local bool bSuccess; maxCandidates = 3; // must be <= size of candidates[] arrays maxDist = 10000; // Initialize the list of candidates for (i=0; i<maxCandidates; i++) { candidates[i].score = -1; candidates[i].dist = maxDist+1; } candidateCount = 0; MoveTarget = None; destPoint = None; if (bAvoidHarm) { GetProjectileList(projList, Location); if (IsLocationDangerous(projList, Location)) { vector1 = ComputeAwayVector(projList); rotator1 = Rotator(vector1); if (AIDirectionReachable(Location, rotator1.Yaw, rotator1.Pitch, CollisionRadius+24, VSize(vector1), destLoc)) return; // eck -- hack!!! } } if (Enemy != None) { foreach RadiusActors(Class'HidePoint', hidePoint, maxDist) { // Can the boogeyman see our hiding spot? if (!enemy.LineOfSightTo(hidePoint)) { // More importantly, can we REACH our hiding spot? waypoint = GetNextWaypoint(hidePoint); if (waypoint != None) { // How far is it to the hiding place? dist = VSize(hidePoint.Location - Location); // Determine vectors to the waypoint and our enemy vector1 = enemy.Location - Location; vector2 = waypoint.Location - Location; // Strip out magnitudes from the vectors tmpDist = VSize(vector1); if (tmpDist > 0) vector1 /= tmpDist; tmpDist = VSize(vector2); if (tmpDist > 0) vector2 /= tmpDist; // Add them vector1 += vector2; // Compute a score (a function of angle) score = VSize(vector1); score = 4-(score*score); // Find an empty slot for this candidate openSlot = -1; bestScore = score; bestDist = dist; for (i=0; i<maxCandidates; i++) { // Can we replace the candidate in this slot? if (bestScore > candidates[i].score) bReplace = TRUE; else if ((bestScore == candidates[i].score) && (bestDist < candidates[i].dist)) bReplace = TRUE; else bReplace = FALSE; if (bReplace) { bestScore = candidates[i].score; bestDist = candidates[i].dist; openSlot = i; } } // We found an open slot -- put our candidate here if (openSlot >= 0) { candidates[openSlot].point = hidePoint; candidates[openSlot].waypoint = waypoint; candidates[openSlot].location = waypoint.Location; candidates[openSlot].score = score; candidates[openSlot].dist = dist; if (candidateCount < maxCandidates) candidateCount++; } } } } // Any candidates? if (candidateCount > 0) { // Find a random candidate // (candidates moving AWAY from the enemy have a higher // probability of being chosen than candidates moving // TOWARDS the enemy) maxScore = 0; for (i=0; i<candidateCount; i++) maxScore += candidates[i].score; score = FRand() * maxScore; for (i=0; i<candidateCount; i++) { score -= candidates[i].score; if (score <= 0) break; } destPoint = candidates[i].point; MoveTarget = candidates[i].waypoint; destLoc = candidates[i].location; } else { iterations = 4; magnitude = 400*(FRand()*0.4+0.8); // 400, +/-20% rotator1 = Rotator(Location-Enemy.Location); if (!AIPickRandomDestination(100, magnitude, rotator1.Yaw, 0.6, rotator1.Pitch, 0.6, iterations, FRand()*0.4+0.35, destLoc)) destLoc = Location+(VRand()*1200); // we give up } } else destLoc = Location+(VRand()*1200); // we give up } function BeginState() { StandUp(); Disable('AnimEnd'); //Disable('Bump'); BlockReactions(); if (!bCower) bCanConverse = False; bStasis = False; SetupWeapon(false, true); SetDistress(true); EnemyReadiness = 1.0; //ReactionLevel = 1.0; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); Enable('AnimEnd'); //Enable('Bump'); ResetReactions(); if (!bCower) bCanConverse = True; bStasis = True; } Begin: //EnemyLastSeen = 0; destPoint = None; Surprise: if ((1.0-ReactionLevel)*SurprisePeriod < 0.25) Goto('Flee'); Acceleration=vect(0,0,0); PlaySurpriseSound(); PlayWaiting(); Sleep(FRand()*0.5); if (Enemy != None) TurnToward(Enemy); if (bCower) Goto('Flee'); Sleep(FRand()*0.5+0.5); Flee: if (bLeaveAfterFleeing) { bTransient = true; bDisappear = true; } if (bCower) Goto('Cower'); WaitForLanding(); PickDestination(); Moving: Sleep(0.0); if (enemy == None) { Acceleration = vect(0,0,0); PlayWaiting(); Sleep(2.0); FinishFleeing(); } // Move from pathnode to pathnode until we get where we're going if (destPoint != None) { EnableCheckDestLoc(true); while (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); if (enemy.bDetectable && enemy.AICanSee(destPoint, 1.0, false, false, false, true) > 0) { PickDestination(); EnableCheckDestLoc(false); Goto('Moving'); } if (MoveTarget == destPoint) break; MoveTarget = FindPathToward(destPoint); } EnableCheckDestLoc(false); } else if (PointReachable(destLoc)) { if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); if (enemy.bDetectable && enemy.AICanSee(Self, 1.0, false, false, true, true) > 0) { PickDestination(); Goto('Moving'); } } else { PickDestination(); Goto('Moving'); } Pausing: Acceleration = vect(0,0,0); if (enemy != None) { if (HidePoint(destPoint) != None) { if (ShouldPlayTurn(Location + HidePoint(destPoint).faceDirection)) PlayTurning(); TurnTo(Location + HidePoint(destPoint).faceDirection); } Enable('AnimEnd'); TweenToWaiting(0.2); while (AICanSee(enemy, 1.0, false, false, true, true) <= 0) Sleep(0.25); Disable('AnimEnd'); FinishAnim(); } Goto('Flee'); Cower: if (!InSeat(useLoc)) Goto('CowerContinue'); PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CowerContinue: Acceleration = vect(0,0,0); PlayCowerBegin(); FinishAnim(); PlayCowering(); // behavior 3 - cower and occasionally make short runs while (true) { Sleep(FRand()*3+6); PlayCowerEnd(); FinishAnim(); if (AIPickRandomDestination(60, 150, 0, 0, 0, 0, 2, FRand()*0.3+0.6, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); } PlayCowerBegin(); FinishAnim(); PlayCowering(); } /* behavior 2 - cower forever // don't stop cowering while (true) Sleep(1.0); */ /* behavior 1 - cower only when enemy watching if (enemy != None) { while (AICanSee(enemy, 1.0, false, false, true, true) > 0) Sleep(0.25); } PlayCowerEnd(); FinishAnim(); Goto('Pausing'); */ ContinueFlee: ContinueFromDoor: FinishAnim(); PlayRunning(); if (bCower) Goto('Cower'); else Goto('Moving'); } // ---------------------------------------------------------------------- // state Attacking // // Kill! Kill! Kill! Kill! // ---------------------------------------------------------------------- State Attacking { function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local Pawn oldEnemy; local bool bHateThisInjury; local bool bFearThisInjury; if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury)) { oldEnemy = Enemy; bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury); bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury); if (bHateThisInjury) IncreaseAgitation(instigatedBy, 1.0); if (bFearThisInjury) IncreaseFear(instigatedBy, 2.0); if (ReadyForNewEnemy()) SetEnemy(instigatedBy); if (ShouldFlee()) { SetDistressTimer(); PlayCriticalDamageSound(); if (RaiseAlarm == RAISEALARM_BeforeFleeing) SetNextState('Alerting'); else SetNextState('Fleeing'); } else { SetDistressTimer(); if (oldEnemy != Enemy) PlayNewTargetSound(); SetNextState('Attacking', 'ContinueAttack'); } GotoDisabledState(damageType, hitPos); } } function SetFall() { StartFalling('Attacking', 'ContinueAttack'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Reloading(DeusExWeapon reloadWeapon, float reloadTime) { Global.Reloading(reloadWeapon, reloadTime); if (bReadyToReload) if (IsWeaponReloading()) if (!IsHandToHand()) TweenToShoot(0); } function EDestinationType PickDestination() { local vector distVect; local vector tempVect; local rotator enemyDir; local float magnitude, dista; local float calcMagnitude; local int iterations; local EDestinationType destType; local NearbyProjectileList projList; destPoint = None; destLoc = vect(0, 0, 0); destType = DEST_Failure; if (enemy == None) return (destType); if ((bCrouching && (CrouchTimer > 0))) destType = DEST_SameLocation; else if (bDefensiveStyle) //CyberP: added: enemies don't ALWAYS rush your position when in close proximity { dista = Abs(VSize(Enemy.Location - Location)); if (dista < 768) if (EnemyTimer < EnemyTimeout*0.3 && EnemyTimer > 0 && Weapon.IsA('DeusExWeapon') && DeusExWeapon(Weapon).AccurateRange > 768) destType = DEST_SameLocation; } if (destType == DEST_Failure) { if (AICanShoot(enemy, true, false, 0.025) || ActorReachable(enemy)) { destType = ComputeBestFiringPosition(tempVect); if (destType == DEST_NewLocation) destLoc = tempVect; } } if (destType == DEST_Failure) { MoveTarget = FindPathToward(enemy); if (MoveTarget != None) { if (!bDefendHome || IsNearHome(MoveTarget.Location)) { if (bAvoidHarm) GetProjectileList(projList, MoveTarget.Location); if (!bAvoidHarm || !IsLocationDangerous(projList, MoveTarget.Location)) { destPoint = MoveTarget; destType = DEST_NewLocation; } } } } // Default behavior, so they don't just stand there... if (destType == DEST_Failure) { enemyDir = Rotator(Enemy.Location - Location); //if (IsHandToHand()) //{destType = DEST_NewLocation; destLoc = Location+(VRand()*1200);} //CyberP: run away if cannot reach player with melee if (AIPickRandomDestination(60, 150, enemyDir.Yaw, 0.5, enemyDir.Pitch, 0.5, 2, FRand()*0.4+0.35, tempVect)) { if (!bDefendHome || IsNearHome(tempVect)) { destType = DEST_NewLocation; destLoc = tempVect; } } } return (destType); } function bool FireIfClearShot() { local DeusExWeapon dxWeapon; dxWeapon = DeusExWeapon(Weapon); if (dxWeapon != None) { if (dxWeapon.bHandToHand) bDefendHome = False; //CyberP: campers no longer camp if out of ammo if ((dxWeapon.AIFireDelay > 0) && (FireTimer > 0)) return false; else if (dxWeapon.IsA('WeaponRifle') && FRand() < 0.8 && Enemy.IsA('DeusExPlayer') && DeusExPlayer(Enemy).CombatDifficulty < 3) return false; //CyberP: rand as to whether snipers can fire, to simulate taking time to aim. else if (AICanShoot(enemy, true, true, 0.025)) { Weapon.Fire(0); FireTimer = dxWeapon.AIFireDelay; return true; } else return false; } else return false; } function CheckAttack(bool bPlaySound) { local bool bCriticalDamage; local bool bOutOfAmmo; local Pawn oldEnemy; local bool bAllianceSwitch; oldEnemy = enemy; bAllianceSwitch = false; if (!IsValidEnemy(enemy)) { if (IsValidEnemy(enemy, false)) bAllianceSwitch = true; SetEnemy(None, 0, true); } if (enemy == None) { if (Orders == 'Attacking') { FindOrderActor(); SetEnemy(Pawn(OrderActor), 0, true); } } if (ReadyForNewEnemy()) FindBestEnemy(false); if (enemy == None) { Enemy = oldEnemy; // hack if (bPlaySound) { if (bAllianceSwitch) PlayAllianceFriendlySound(); else PlayAreaSecureSound(); } Enemy = None; if (Orders != 'Attacking') FollowOrders(); else GotoState('Wandering'); return; } SwitchToBestWeapon(); if (bCrouching && (CrouchTimer <= 0) && !ShouldCrouch()) { EndCrouch(); TweenToShoot(0.15); } bCriticalDamage = False; bOutOfAmmo = False; if (ShouldFlee()) bCriticalDamage = True; else if (Weapon == None) bOutOfAmmo = True; else if (Weapon.ReloadCount > 0) { if (Weapon.AmmoType == None) bOutOfAmmo = True; else if (Weapon.AmmoType.AmmoAmount < 1) bOutOfAmmo = True; } if (bCriticalDamage || bOutOfAmmo) { if (bPlaySound) { if (bCriticalDamage) PlayCriticalDamageSound(); else if (bOutOfAmmo) PlayOutOfAmmoSound(); } if (RaiseAlarm == RAISEALARM_BeforeFleeing) GotoState('Alerting'); else GotoState('Fleeing'); } else if (bPlaySound && (oldEnemy != Enemy)) PlayNewTargetSound(); } function Tick(float deltaSeconds) { local bool bCanSee; local float yaw; local vector lastLocation; local Pawn lastEnemy; local float surpriseTime; Global.Tick(deltaSeconds); if (CrouchTimer > 0) { CrouchTimer -= deltaSeconds; if (CrouchTimer < 0) CrouchTimer = 0; } EnemyTimer += deltaSeconds; UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false); if (IsA('Greasel')) { ShouldGreaselMelee(); } if ((Enemy != None) && HasEnemyTimedOut()) { lastLocation = Enemy.Location; lastEnemy = Enemy; FindBestEnemy(true); if (Enemy == None) { SetSeekLocation(lastEnemy, lastLocation, SEEKTYPE_Guess, true); GotoState('Seeking'); } } else if (bCanFire && (Enemy != None)) { ViewRotation = Rotator(Enemy.Location-Location); if (bFacingTarget) FireIfClearShot(); else if (!bMustFaceTarget) { yaw = (ViewRotation.Yaw-Rotation.Yaw) & 0xFFFF; if (yaw >= 32768) yaw -= 65536; yaw = Abs(yaw)*360/32768; // 0-180 x 2 if (yaw <= FireAngle) FireIfClearShot(); } } //UpdateReactionLevel(true, deltaSeconds); } function bool IsHandToHand() { if (Weapon != None) { if (DeusExWeapon(Weapon) != None) { if (DeusExWeapon(Weapon).bHandToHand && !DeusExWeapon(Weapon).IsA('WeaponHideAGun')) return true; else return false; } else return false; } else return false; } function bool ReadyForWeapon() { local bool bReady; bReady = false; if (DeusExWeapon(weapon) != None) { if (DeusExWeapon(weapon).bReadyToFire) if (!IsWeaponReloading()) bReady = true; } if (!bReady) if (enemy == None) bReady = true; if (!bReady) if (!AICanShoot(enemy, true, false, 0.025)) bReady = true; return (bReady); } function bool ShouldCrouch() { if (bCanCrouch && !Region.Zone.bWaterZone && !IsHandToHand() && ((enemy != None) && (VSize(enemy.Location-Location) > 100)) && //CyberP: 300 ((DeusExWeapon(Weapon) == None) || DeusExWeapon(Weapon).bUseWhileCrouched)) return true; else return false; } function StartCrouch() { if (!bCrouching) { bCrouching = true; SetBasedPawnSize(CollisionRadius, GetCrouchHeight()); CrouchTimer = 1.0+FRand()*0.75; //CyberP: was *0.5 } } function EndCrouch() { if (bCrouching) { bCrouching = false; ResetBasedPawnSize(); } } function SystemShocked() { local Vector vec; local ShockRingProjectile shock; if (AnimSequence == 'Pickup') { vec = Location; vec.Z += 10; shock = Spawn(class'ShockRingProjectile',,, vec, rot(16384,0,0)); if (shock!=none) {shock.ScaleGlow=0.0001; shock.bUnlit=False;} shock = Spawn(class'ShockRingProjectile',,, vec, rot(16384,0,0)); if (shock!=none) {shock.ScaleGlow=0.0001; shock.bUnlit=False;} Spawn(class'ShockRingProjectile',,, vec, rot(16384,0,0)); Spawn(class'SphereEffectShield',,, vec); PlaySound(sound'shootrail',SLOT_None,2,,2048); } bRadialBlastClamp=True; //SetPhysics(PHYS_Walking); } function ShouldMelee() { local DeusExPlayer dxPlayer; local vector HitNormal, HitLocation, StartTrace, EndTrace; local float dista; dxPlayer = DeusExPlayer(GetPlayerPawn()); dista = Abs(VSize(dxPlayer.Location - Location)); //CyberP: I should probably change this to apply to enemy rather than just dxPlayer if (dista < 128) { StartTrace = Location; EndTrace = Location + (Vector(Rotation)) * (CollisionRadius+40); if (dxPlayer != None && Trace(HitLocation, HitNormal, EndTrace, StartTrace, True) == dxPlayer) { if (Physics == PHYS_Walking && !Region.Zone.bWaterZone) { bCommandoMelee = True; if (IsA('MJ12Commando')) PlaySound(Sound'TurretLocked',SLOT_None,0.5,,512,1.2); } } } } function ShouldGreaselMelee() //CyberP: TODO: trace a few feet up for geometry so greasel doesn't jump in vents etc? { local vector HitNormal, HitLocation, StartTrace, EndTrace; local float dista; dista = Abs(VSize(enemy.Location - Location)); if (!Region.Zone.bWaterZone && Physics != PHYS_Falling && AnimSequence != 'Attack') { if (dista < 288 && dista > 96) { StartTrace = Location; EndTrace = Location + (Vector(Rotation)) * 288; if (Trace(HitLocation, HitNormal, EndTrace, StartTrace, True) == enemy) { bGreaselShould = True; } else { bGreaselShould = False; } } } } function ShouldRadialBlast() { local float dist; if (GroundSpeed > default.GroundSpeed && FRand() < 0.015) { if (bRadialBlastClamp) { if (Enemy!=none) { dist = Abs(VSize(Enemy.Location - Location)); //CyberP: should change this to trace so they cannot do so through walls. if (dist < 320 && (Abs(Location.Z-Enemy.Location.Z) < 96)) { bRadialBlastClamp=False; Velocity = vect(0,0,1); PlayAnimPivot('Pickup',0.6,0.1); } } } } } function checkNadeThrow() { local DeusExPlayer playa; local HumanMilitary P; local float dista; local Vector HitNormal, HitLocation, StartTrace, EndTrace, offset; local int i; if (Enemy != None && Enemy.IsA('DeusExPlayer')) playa = DeusExPlayer(Enemy); if (playa != none && bCanNade == False) { //CyberP: trace towards player. If we manage to trace end near the player AND there is no other pawns close to that location, then we can throw. dista = Abs(VSize(playa.Location - Location)); if (dista > 768 && dista < 1536 && (Abs(Location.Z-playa.Location.Z) < 160)) { offset = Location; offset.Z += 8; StartTrace = offset; EndTrace = playa.Location; ForEach Trace(HitLocation, HitNormal, EndTrace, StartTrace, True).RadiusActors(Class'DeusExPlayer',playa,320,HitLocation) { i++; if (i == 1) { i = 0; ForEach playa.RadiusActors(Class'HumanMilitary',P,640,playa.Location) { i++; } if (i < 1) bCanNade=True; } } } } } function nadeThrow() //CyberP: the throwing itself is exceptionally bad, but hey, it is still better than the vanilla nade throwing. { local GasGrenade gasNade; local EMPGrenade empNade; local vector flareOffset; local DeusExPlayer playa; local float dista; flareOffset = Location; flareOffset.Z += 50; playa = DeusExPlayer(GetPlayerPawn()); dista = Abs(VSize(playa.Location - Location)); if (IsA('Terrorist') || IsA('UNATCOTroop') || IsA('RiotCop')) { gasNade = Spawn(class'GasGrenade',self,,flareOffset); if (gasNade != None) { if (playa != None && playa.CombatDifficulty > 2) gasNade.blastRadius += 256; if (dista < 1000) { gasNade.Velocity = Vector(Rotation)*1000 + (VRand()*6); gasNade.Velocity.Z += 300; } else if (dista < 1300) { gasNade.Velocity = Vector(Rotation)*1150 + (VRand()*6); gasNade.Velocity.Z += 350; } else { gasNade.Velocity = Vector(Rotation)*1300 + (VRand()*6); gasNade.Velocity.Z += 400; } } } else if (IsA('MJ12Troop') || IsA('Soldier') || IsA('MJ12Elite')) { if (FRand() < 0.5) { gasNade = Spawn(class'GasGrenade',self,,flareOffset); if (gasNade != None) { if (playa != None && playa.CombatDifficulty > 2) gasNade.blastRadius += 256; if (dista < 900) { gasNade.Velocity = Vector(Rotation)*1000 + (VRand()*6); gasNade.Velocity.Z += 300; } else if (dista < 1300) { gasNade.Velocity = Vector(Rotation)*1150 + (VRand()*6); gasNade.Velocity.Z += 350; } else { gasNade.Velocity = Vector(Rotation)*1300 + (VRand()*6); gasNade.Velocity.Z += 400; } } } else { empNade = Spawn(class'EMPGrenade',self,,flareOffset); if (empNade != None) { if (playa != None && playa.CombatDifficulty > 2) empNade.blastRadius += 256; if (dista < 900) { empNade.Velocity = Vector(Rotation)*1000 + (VRand()*6); empNade.Velocity.Z += 300; } else if (dista < 1300) { empNade.Velocity = Vector(Rotation)*1150 + (VRand()*6); empNade.Velocity.Z += 350; } else { empNade.Velocity = Vector(Rotation)*1300 + (VRand()*6); empNade.Velocity.Z += 400; } } } } } function BeginState() { StandUp(); // hack if (MaxRange < MinRange+10) MaxRange = MinRange+10; bCanFire = false; bFacingTarget = false; SwitchToBestWeapon(); //EnemyLastSeen = 0; BlockReactions(); bCanConverse = False; bAttacking = True; bStasis = False; SetDistress(true); CrouchTimer = 0; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bCanFire = false; bFacingTarget = false; ResetReactions(); bCanConverse = True; bAttacking = False; bStasis = True; bReadyToReload = false; EndCrouch(); } Begin: if (Enemy == None) GotoState('Seeking'); //EnemyLastSeen = 0; CheckAttack(false); Surprise: if ((1.0-ReactionLevel)*SurprisePeriod < 0.25) Goto('BeginAttack'); Acceleration=vect(0,0,0); PlaySurpriseSound(); PlayWaiting(); while (ReactionLevel < 1.0) { TurnToward(Enemy); Sleep(0); } BeginAttack: EnemyReadiness = 1.0; ReactionLevel = 1.0; if (PlayerAgitationTimer > 0) PlayAllianceHostileSound(); else PlayTargetAcquiredSound(); if (PlayBeginAttack()) { Acceleration = vect(0,0,0); TurnToward(enemy); FinishAnim(); } RunToRange: bCanFire = false; bFacingTarget = false; bReadyToReload = false; EndCrouch(); if (Physics == PHYS_Falling) TweenToRunning(0.05); WaitForLanding(); if (!IsWeaponReloading() || bCrouching) { if (ShouldPlayTurn(Enemy.Location)) PlayTurning(); TurnToward(enemy); } else Sleep(0); bCanFire = true; while (PickDestination() == DEST_NewLocation) { if (bCanStrafe && ShouldStrafe()) { PlayRunningAndFiring(); if (destPoint != None) StrafeFacing(destPoint.Location, enemy); else StrafeFacing(destLoc, enemy); bFacingTarget = true; if (bGrenadier) //CyberP: nade throw { if (FRand() < 0.025) checkNadeThrow(); if (bCanNade) { PlayAnimPivot('Attack',1.1,0.1); Sleep(0.3); nadeThrow(); bCanNade = False; FinishAnim(); PlayWaiting(); Sleep(0.1); } } } else { bFacingTarget = false; PlayRunning(); if (IsA('Greasel')) { if (bGreaselShould) { FinishAnim(); bBiteClamp = False; bCanFire = False; RotationRate.Yaw = 2; switchToBestWeapon(); PlayAnimPivot('Attack',2.6,0.1); SetPhysics(PHYS_Falling); Velocity = (Vector(Rotation)) * 400; Velocity.Z = 260 + (FRand()*20); bGreaselShould=False; Sleep(0.3); FinishAnim(); LoopAnimPivot('Run',runAnimMult,0.1); } } if (destPoint != None) MoveToward(destPoint, MaxDesiredSpeed); else MoveTo(destLoc, MaxDesiredSpeed); } CheckAttack(true); } Fire: bCanFire = false; bFacingTarget = false; Acceleration = vect(0, 0, 0); SwitchToBestWeapon(); if (FRand() > 0.5) bUseSecondaryAttack = true; else bUseSecondaryAttack = false; if (IsHandToHand()) TweenToAttack(0.15); //CyberP: 0.15 else if (ShouldCrouch() && (FRand() < CrouchRate)) { TweenToCrouchShoot(0.15); FinishAnim(); StartCrouch(); } else TweenToShoot(0.15); if (!IsWeaponReloading() || bCrouching) TurnToward(enemy); FinishAnim(); if (IsA('HumanMilitary')) { ShouldMelee(); if (bCommandoMelee) { bCommandoMelee = False; FinishAnim(); if (IsA('MJ12Commando')) PlayAnimPivot('Attack',1.8,0.1); else PlayAnimPivot('Attack',1.4,0.1); sleep(0.3); if (!IsInState('Dying') && !IsInState('TakingHit')) { Enemy.TakeDamage(8,self,Enemy.Location,vect(0,0,0),'Shot'); if (Enemy.IsA('DeusExPlayer')) DeusExPlayer(Enemy).ShakeView(0.2,512,12); Enemy.SetPhysics(PHYS_Falling); if (IsA('MJ12Commando')) Enemy.Velocity -= Vector(Enemy.ViewRotation) * 900; else Enemy.Velocity -= Vector(Enemy.ViewRotation) * 350; Enemy.Velocity.Z = 20; Enemy.PlaySound(sound'pl_fallpain3',SLOT_None,2.0,,,1.2); sleep(0.05); FinishAnim(); } } } if (IsA('Greasel')) { if (bGreaselShould) { FinishAnim(); bBiteClamp = False; bCanFire = False; RotationRate.Yaw = 2; switchToBestWeapon(); PlayAnimPivot('Attack',2.6,0.1); SetPhysics(PHYS_Falling); Velocity = (Vector(Rotation)) * 400; Velocity.Z = 260 + (FRand()*20); bGreaselShould=False; Sleep(0.3); FinishAnim(); LoopAnimPivot('Run',runAnimMult,0.1); } } else if (bGrenadier) //CyberP: nade throw { if ((IsA('MJ12Troop') || IsA('MJ12Elite')) && FRand() < 0.03) checkNadeThrow(); else if (FRand() < 0.02) checkNadeThrow(); if (bCanNade) { PlayAnimPivot('Attack',1.1,0.1); Sleep(0.3); nadeThrow(); bCanNade = False; FinishAnim(); PlayWaiting(); Sleep(0.1); } } else if (IsA('MIB')) { ShouldRadialBlast(); if (AnimSequence == 'Pickup') { sleep(0.7); SystemShocked(); } } bReadyToReload = true; ContinueFire: while (!ReadyForWeapon()) { if (IsA('Greasel')) { if (bGreaselShould) { FinishAnim(); bBiteClamp = False; bCanFire = False; RotationRate.Yaw = 2; switchToBestWeapon(); PlayAnimPivot('Attack',2.6,0.1); SetPhysics(PHYS_Falling); Velocity = (Vector(Rotation)) * 400; Velocity.Z = 260 + (FRand()*20); bGreaselShould=False; Sleep(0.3); FinishAnim(); LoopAnimPivot('Run',runAnimMult,0.1); } } if (PickDestination() != DEST_SameLocation) Goto('RunToRange'); CheckAttack(true); if (!IsWeaponReloading() || bCrouching) TurnToward(enemy); else Sleep(0); } CheckAttack(true); if (!FireIfClearShot()) Goto('ContinueAttack'); bReadyToReload = false; if (bCrouching) PlayCrouchShoot(); else if (IsHandToHand()) PlayAttack(); else PlayShoot(); FinishAnim(); if (IsA('MIB')) { ShouldRadialBlast(); if (AnimSequence == 'Pickup') { sleep(0.7); SystemShocked(); } } if (IsA('HumanMilitary')) { ShouldMelee(); if (bCommandoMelee) { bCommandoMelee = False; FinishAnim(); if (IsA('MJ12Commando')) PlayAnimPivot('Attack',1.8,0.1); else PlayAnimPivot('Attack',1.5,0.1); sleep(0.35); if (!IsInState('Dying') && !IsInState('TakingHit')) { Enemy.TakeDamage(8,self,Enemy.Location,vect(0,0,0),'Shot'); if (Enemy.IsA('DeusExPlayer')) DeusExPlayer(Enemy).ShakeView(0.2,512,12); Enemy.SetPhysics(PHYS_Falling); if (IsA('MJ12Commando')) Enemy.Velocity -= Vector(Enemy.ViewRotation) * 900; else Enemy.Velocity -= Vector(Enemy.ViewRotation) * 400; Enemy.Velocity.Z = 20; Enemy.PlaySound(sound'pl_fallpain3',SLOT_None,2.0,,,1.2); sleep(0.05); FinishAnim(); } } } if (bGrenadier) //CyberP: nade throw { if ((IsA('MJ12Troop') || IsA('MJ12Elite')) && FRand() < 0.03) checkNadeThrow(); else if (FRand() < 0.02) checkNadeThrow(); if (bCanNade) { PlayAnimPivot('Attack',1.1,0.1); Sleep(0.3); nadeThrow(); bCanNade = False; FinishAnim(); PlayWaiting(); Sleep(0.1); } } if (FRand() > 0.5) bUseSecondaryAttack = true; else bUseSecondaryAttack = false; bReadyToReload = true; if (!IsHandToHand()) { if (bCrouching) TweenToCrouchShoot(0); else TweenToShoot(0); } CheckAttack(true); if (PickDestination() != DEST_NewLocation) { if (!IsWeaponReloading() || bCrouching) TurnToward(enemy); else Sleep(0); Goto('ContinueFire'); } Goto('RunToRange'); ContinueAttack: ContinueFromDoor: CheckAttack(true); if (PickDestination() != DEST_NewLocation) Goto('Fire'); else Goto('RunToRange'); } // ---------------------------------------------------------------------- // state Alerting // // Warn other NPCs that an enemy is about // ---------------------------------------------------------------------- State Alerting { function SetFall() { StartFalling('Alerting', 'ContinueAlert'); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { if (bAcceptBump) { if (bumper == AlarmActor) { bAcceptBump = False; GotoState('Alerting', 'SoundAlarm'); } } // Handle conversations, if need be Global.Bump(bumper); } function bool IsAlarmReady(Actor actorAlarm) { local bool bReady; local AlarmUnit alarm; bReady = false; alarm = AlarmUnit(actorAlarm); if ((alarm != None) && !alarm.bDeleteMe) if (!alarm.bActive) if ((alarm.associatedPawn == None) || (alarm.associatedPawn == self)) bReady = true; return bReady; } function TriggerAlarm() { local DeusExPlayer playa; if ((AlarmActor != None) && !AlarmActor.bDeleteMe) { playa = DeusExPlayer(GetPlayerPawn()); if (AlarmActor.hackStrength > 0) // make sure the alarm hasn't been hacked AlarmActor.Trigger(self, Enemy); else if (playa != None && playa.PerkNamesArray[10] == 1) //CyberP: shock the pawn TakeDamage(200,self,vect(0,0,0),vect(0,0,0),'Stunned'); } } function bool IsAlarmInRange(AlarmUnit alarm) { local bool bInRange; bInRange = false; if ((alarm != None) && !alarm.bDeleteMe) if ((VSize((alarm.Location-Location)*vect(1,1,0)) < (CollisionRadius+alarm.CollisionRadius+24)) && (Abs(alarm.Location.Z-Location.Z) < (CollisionHeight+alarm.CollisionHeight))) bInRange = true; return (bInRange); } function vector FindAlarmPosition(Actor alarm) { local vector alarmPos; alarmPos = alarm.Location; alarmPos += vector(alarm.Rotation.Yaw*rot(0,1,0))*(CollisionRadius+alarm.CollisionRadius); return (alarmPos); } function bool GetNextAlarmPoint(AlarmUnit alarm) { local vector alarmPoint; local bool bValid; destPoint = None; destLoc = vect(0,0,0); bValid = false; if ((alarm != None) && !alarm.bDeleteMe) { alarmPoint = FindAlarmPosition(alarm); if (PointReachable(alarmPoint)) { destLoc = alarmPoint; bValid = true; } else { MoveTarget = FindPathTo(alarmPoint); if (MoveTarget != None) { destPoint = MoveTarget; bValid = true; } } } return (bValid); } function AlarmUnit FindTarget() { local ScriptedPawn pawnAlly; local AlarmUnit alarm; local float dist; local AlarmUnit bestAlarm; local float bestDist; bestAlarm = None; // Do we have any allies on this level? foreach AllActors(Class'ScriptedPawn', pawnAlly) if (GetPawnAllianceType(pawnAlly) == ALLIANCE_Friendly) break; // Yes, so look for an alarm box that isn't active... if (pawnAlly != None) { foreach RadiusActors(Class'AlarmUnit', alarm, 3200) //CyberP: was 2400 { if (GetAllianceType(alarm.Alliance) != ALLIANCE_Hostile) { dist = VSize((Location-alarm.Location)*vect(1,1,2)); // use squished sphere if ((bestAlarm == None) || (dist < bestDist)) { bestAlarm = alarm; bestDist = dist; } } } // Is the nearest alarm already going off? And can we reach it? if (!IsAlarmReady(bestAlarm) || !GetNextAlarmPoint(bestAlarm)) bestAlarm = None; } // Return our target alarm box return (bestAlarm); } function bool PickDestination() { local bool bDest; local AlarmUnit alarm; // Init destPoint = None; destLoc = vect(0, 0, 0); bDest = false; // Find an alarm we can trigger alarm = FindTarget(); if (alarm != None) { // Find a way to get there AlarmActor = alarm; alarm.associatedPawn = self; bDest = true; // if alarm != none, we've already computed the route to the alarm } // Return TRUE if we were successful return (bDest); } function BeginState() { StandUp(); //Disable('AnimEnd'); bAcceptBump = False; bCanConverse = False; AlarmActor = None; bStasis = False; BlockReactions(); SetupWeapon(false); SetDistress(false); EnemyReadiness = 1.0; ReactionLevel = 1.0; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bAcceptBump = False; //Enable('AnimEnd'); bCanConverse = True; if (AlarmActor != None) if (AlarmActor.associatedPawn == self) AlarmActor.associatedPawn = None; AlarmActor = None; bStasis = True; } Begin: if (Enemy == None && RaiseAlarm != RAISEALARM_BeforeAttacking) //CyberP: added extra condition for pawns who spot carci GotoState('Seeking'); //EnemyLastSeen = 0; destPoint = None; if (RaiseAlarm == RAISEALARM_Never) GotoState('Fleeing'); if (AlarmTimer > 0) PlayGoingForAlarmSound(); Alert: if (AlarmTimer > 0) Goto('Done'); WaitForLanding(); if (!PickDestination()) Goto('Done'); Moving: // Can we go somewhere? bAcceptBump = True; EnableCheckDestLoc(true); while (true) { if (destPoint != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } else { if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); CheckDestLoc(destLoc); } if (IsAlarmInRange(AlarmActor)) break; else if (!GetNextAlarmPoint(AlarmActor)) break; } EnableCheckDestLoc(false); SoundAlarm: Acceleration=vect(0,0,0); bAcceptBump = False; if (IsAlarmInRange(AlarmActor)) { TurnToward(AlarmActor); PlayPushing(); FinishAnim(); TriggerAlarm(); } Done: bAcceptBump = False; if (RaiseAlarm == RAISEALARM_BeforeAttacking) GotoState('Attacking'); else GotoState('Fleeing'); ContinueAlert: ContinueFromDoor: Goto('Alert'); } // ---------------------------------------------------------------------- // state Shadowing // // Quietly tail another character // ---------------------------------------------------------------------- State Shadowing { function SetFall() { StartFalling('Shadowing', 'ContinueShadow'); } function Tick(float deltaSeconds) { local bool bMove; local float deltaValue; Global.Tick(deltaSeconds); deltaValue = deltaSeconds; // If we're running, and we can see our target, STOP RUNNING! if (bRunningStealthy) { UpdateActorVisibility(orderActor, deltaValue, 0.0, false); deltaValue = 0; if (EnemyLastSeen <= 0) { bRunningStealthy = False; PlayWalking(); DesiredSpeed = GetWalkingSpeed(); } } // Are we stopped? if (bPausing) { // Can we see our target? bMove = False; UpdateActorVisibility(orderActor, deltaValue, 0.5, false); deltaValue = 0; // No -- move toward him! if (EnemyLastSeen > 0.5) bMove = True; // We can see him, and we're staring... else if (bStaring) { // ...can he see us staring at him? if ((Pawn(orderActor) != None) && (Pawn(orderActor).AICanSee(self, , false, true, false, false) > 0)) bMove = True; // Time to look inconspicuous } // Move if we need to if (bMove) { if (bStaring) GotoState('Shadowing', 'StopStaring'); else GotoState('Shadowing', 'StopPausing'); bPausing = False; bStaring = False; } } } function Bump(actor bumper) { if (bAcceptBump) { // If we get bumped by another actor while we wait, start wandering again bAcceptBump = False; bPausing = False; bStaring = False; Disable('AnimEnd'); GotoState('Shadowing', 'Shadow'); } // Handle conversations, if need be Global.Bump(bumper); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function float DistanceToTarget() { return (VSize(Location-orderActor.Location)); } function bool PickDestination() { local Actor destActor; local Vector distVect; local Rotator relativeRotation; local float magnitude; local float minDist; local float maxDist; local float bestDist; local bool bDest; local float dist; // Init destPoint = None; destLoc = vect(0, 0, 0); // Conveniences destActor = orderActor; minDist = 400; maxDist = 700; bestDist = (maxDist+minDist)*0.5; distVect = Location - destActor.Location; magnitude = VSize(distVect); bDest = False; // Can we see the target? if (AICanSee(destActor, , false, false, false, true) > 0) { relativeRotation = Rotator(distVect); // How far will we go? dist = (wanderlust*300+150) * (FRand()*0.2+0.9); // 150-450, +/-10% // Move around inconspicuously, like we're just wandering if (magnitude < minDist) // too close -- move away bDest = AIPickRandomDestination(100, dist, relativeRotation.Yaw, 0.8, relativeRotation.Pitch, 0.8, 3, FRand()*0.4+0.35, destLoc); else if (magnitude < maxDist) // just right -- move normally bDest = AIPickRandomDestination(100, dist, relativeRotation.Yaw+32768, 0, -relativeRotation.Pitch, 0, 2, FRand()*0.4+0.35, destLoc); else // too far -- move closer bDest = AIPickRandomDestination(100, dist, relativeRotation.Yaw+32768, 0.8, -relativeRotation.Pitch, 0.8, 3, FRand()*0.4+0.35, destLoc); } // Nope -- find a path towards him else { MoveTarget = FindPathToward(destActor); if (MoveTarget != None) { if (!MoveTarget.Region.Zone.bWaterZone && (MoveTarget.Physics != PHYS_Falling)) { destPoint = MoveTarget; bDest = True; } } } // Return TRUE if we found a place to go return (bDest); } function BeginState() { StandUp(); Disable('AnimEnd'); bRunningStealthy = False; bPausing = False; bStaring = False; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = False; Enable('AnimEnd'); bRunningStealthy = False; bPausing = False; bStaring = False; bStasis = True; } Begin: EnemyLastSeen = 0; destPoint = None; Shadow: WaitForLanding(); Moving: Sleep(0.0); // Can we go somewhere? if (PickDestination()) { // Are we going to a navigation point? if (destPoint != None) { if (MoveTarget != None) { // Run if we're too far away, and we can't see our target if ((DistanceToTarget() > 900) && (AICanSee(orderActor, , false, true, true, true) <= 0)) { bRunningStealthy = True; if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); } // Otherwise, walk nonchalantly else { bRunningStealthy = False; if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); } } } // No pathnode, so walk to a point else { bRunningStealthy = False; if (ShouldPlayWalk(destLoc)) PlayWalking(); MoveTo(destLoc, GetWalkingSpeed()); } } // Can we see the target? If not, keep walking if (AICanSee(orderActor, , false, false, false, true) <= 0) Goto('Moving'); Pausing: // Stop bRunningStealthy = False; Acceleration = vect(0, 0, 0); // Can the target see us? If not, stare! if (orderActor.IsA('Pawn') && Pawn(orderActor).AICanSee(self, , false, true, false, false) <= 0) Goto('Staring'); // Stop normally sleepTime = 6.0; Enable('AnimEnd'); TweenToWaiting(0.2); bAcceptBump = True; sleepTime *= (-0.9*restlessness) + 1; bStaring = False; bPausing = True; Sleep(sleepTime); StopPausing: // Time to move again bPausing = False; bStaring = False; Disable('AnimEnd'); bAcceptBump = False; FinishAnim(); Goto('Shadow'); Staring: // Stare at the target PlayTurning(); TurnToward(orderActor); Enable('AnimEnd'); TweenToWaiting(0.2); // Don't move 'til he looks at us bAcceptBump = True; bStaring = True; bPausing = True; while (true) { PlayTurning(); TurnToward(orderActor); TweenToWaiting(0.2); Sleep(0.25); } StopStaring: // He's looking, or we can't see him -- time to move bPausing = False; bStaring = False; Disable('AnimEnd'); bAcceptBump = False; FinishAnim(); Goto('Shadow'); ContinueShadow: ContinueFromDoor: FinishAnim(); PlayRunning(); Goto('Moving'); } // ---------------------------------------------------------------------- // state Following // // Follow an actor // ---------------------------------------------------------------------- state Following { function SetFall() { StartFalling('Following', 'ContinueFollow'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); if (BackpedalTimer >= 0) BackpedalTimer += deltaSeconds; animTimer[1] += deltaSeconds; if ((Physics == PHYS_Walking) && (orderActor != None)) { if (Acceleration == vect(0,0,0)) LookAtActor(orderActor, true, true, true, 0, 0.25); else PlayTurnHead(LOOK_Forward, 1.0, 0.25); } } function bool PickDestination() { local float dist; local float extra; local float distMax; local int dir; local rotator rot; local bool bSuccess; bSuccess = false; destPoint = None; destLoc = vect(0, 0, 0); extra = orderActor.CollisionRadius + CollisionRadius; dist = VSize(orderActor.Location - Location); dist -= extra; if (dist < 0) dist = 0; if ((dist > 180) || (AICanSee(orderActor, , false, false, false, true) <= 0)) { if (ActorReachable(orderActor)) { rot = Rotator(orderActor.Location - Location); distMax = (dist-180)+45; if (distMax > 80) distMax = 80; bSuccess = AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 0, distMax, destLoc); } else { MoveTarget = FindPathToward(orderActor); if (MoveTarget != None) { destPoint = MoveTarget; bSuccess = true; } } BackpedalTimer = -1; } else if (dist < 60) { if (BackpedalTimer < 0) BackpedalTimer = 0; if (BackpedalTimer > 1.0) // give the player enough time to converse, if he wants to { rot = Rotator(Location - orderActor.Location); bSuccess = AIDirectionReachable(orderActor.Location, rot.Yaw, rot.Pitch, 60+extra, 120+extra, destLoc); } } else BackpedalTimer = -1; return (bSuccess); } function BeginState() { StandUp(); //Disable('AnimEnd'); bStasis = False; SetupWeapon(false); SetDistress(false); BackpedalTimer = -1; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = False; //Enable('AnimEnd'); bStasis = True; StopBlendAnims(); } Begin: Acceleration = vect(0, 0, 0); destPoint = None; if (orderActor == None) GotoState('Standing'); if (!PickDestination()) Goto('Wait'); Follow: if (destPoint != None) { if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } else Sleep(0.0); // this shouldn't happen } else { if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); CheckDestLoc(destLoc); } if (PickDestination()) Goto('Follow'); Wait: //PlayTurning(); //TurnToward(orderActor); PlayWaiting(); WaitLoop: Acceleration=vect(0,0,0); Sleep(0.0); if (!PickDestination()) Goto('WaitLoop'); else Goto('Follow'); ContinueFollow: ContinueFromDoor: Acceleration=vect(0,0,0); if (PickDestination()) Goto('Follow'); else Goto('Wait'); } // ---------------------------------------------------------------------- // state WaitingFor // // Wait for a pawn to become visible, then move to it // ---------------------------------------------------------------------- state WaitingFor { function SetFall() { StartFalling('WaitingFor', 'ContinueFollow'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { // If we hit the guy we're going to, end the state if (bumper == OrderActor) GotoState('WaitingFor', 'Done'); // Handle conversations, if need be Global.Bump(bumper); } function Touch(actor toucher) { // If we hit the guy we're going to, end the state if (toucher == OrderActor) GotoState('WaitingFor', 'Done'); // Handle conversations, if need be Global.Touch(toucher); } function BeginState() { StandUp(); //BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = True; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); //ResetReactions(); bStasis = True; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) GotoState('Idle'); PlayWaiting(); Wait: Sleep(1.0); if (AICanSee(orderActor, 1.0, false, true, false, true) <= 0) Goto('Wait'); bStasis = False; Follow: if (IsOverlapping(orderActor)) Goto('Done'); MoveTarget = GetNextWaypoint(orderActor); if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) && (MoveTarget.Physics != PHYS_Falling)) { if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn')) { if (GetNextVector(orderActor, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CheckDestLoc(useLoc); } else Goto('Pause'); } else { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } if (IsOverlapping(orderActor)) Goto('Done'); else Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: GotoState('Standing'); ContinueFollow: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state GoingTo // // Move to an actor. // ---------------------------------------------------------------------- state GoingTo { function SetFall() { StartFalling('GoingTo', 'ContinueGo'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { // If we hit the guy we're going to, end the state if (bumper == OrderActor) GotoState('GoingTo', 'Done'); // Handle conversations, if need be Global.Bump(bumper); } function Touch(actor toucher) { // If we hit the guy we're going to, end the state if (toucher == OrderActor) GotoState('GoingTo', 'Done'); // Handle conversations, if need be Global.Touch(toucher); } function BeginState() { StandUp(); //BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = False; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); //ResetReactions(); bStasis = True; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: if (IsOverlapping(orderActor)) Goto('Done'); MoveTarget = GetNextWaypoint(orderActor); if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) && (MoveTarget.Physics != PHYS_Falling)) { if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn')) { if (GetNextVector(orderActor, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayWalking(); MoveTo(useLoc, GetWalkingSpeed()); CheckDestLoc(useLoc); } else Goto('Pause'); } else { if (ShouldPlayWalk(MoveTarget.Location)) PlayWalking(); MoveToward(MoveTarget, GetWalkingSpeed()); CheckDestLoc(MoveTarget.Location, true); } if (IsOverlapping(orderActor)) Goto('Done'); else Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (orderActor.IsA('PatrolPoint')) TurnTo(Location + PatrolPoint(orderActor).lookdir); GotoState('Standing'); ContinueGo: ContinueFromDoor: PlayWalking(); Goto('Follow'); } // ---------------------------------------------------------------------- // state RunningTo // // Move to an actor really fast. // ---------------------------------------------------------------------- state RunningTo { function SetFall() { StartFalling('RunningTo', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function Bump(actor bumper) { // If we hit the guy we're going to, end the state if (bumper == OrderActor) GotoState('RunningTo', 'Done'); // Handle conversations, if need be Global.Bump(bumper); } function Touch(actor toucher) { // If we hit the guy we're going to, end the state if (toucher == OrderActor) GotoState('RunningTo', 'Done'); // Handle conversations, if need be Global.Touch(toucher); } function BeginState() { StandUp(); //BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = False; SeekPawn = None; EnableCheckDestLoc(true); } function EndState() { EnableCheckDestLoc(false); //ResetReactions(); bStasis = True; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: if (IsOverlapping(orderActor)) Goto('Done'); MoveTarget = GetNextWaypoint(orderActor); if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) && (MoveTarget.Physics != PHYS_Falling)) { if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn')) { if (GetNextVector(orderActor, useLoc)) { if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); CheckDestLoc(useLoc); } else Goto('Pause'); } else { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, MaxDesiredSpeed); CheckDestLoc(MoveTarget.Location, true); } if (IsOverlapping(orderActor)) Goto('Done'); else Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (orderActor.IsA('PatrolPoint')) TurnTo(Location + PatrolPoint(orderActor).lookdir); GotoState('Standing'); ContinueRun: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state DebugFollowing // // Following state used for pathnode testing // ---------------------------------------------------------------------- state DebugFollowing { function SetFall() { StartFalling('DebugFollowing', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function BeginState() { StandUp(); BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = false; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bStasis = true; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: MoveTarget = GetNextWaypoint(orderActor); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, 1.0); Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (HasNextState()) GotoNextState(); else GotoState('Standing'); ContinueRun: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state DebugPathfinding // // Following state used for pathnode testing // ---------------------------------------------------------------------- state DebugPathfinding { function SetFall() { StartFalling('DebugPathfinding', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function BeginState() { StandUp(); BlockReactions(); SetupWeapon(false); SetDistress(false); bStasis = false; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bStasis = true; } Begin: Acceleration = vect(0, 0, 0); if (orderActor == None) Goto('Done'); Follow: MoveTarget = FindPathToward(orderActor); if (MoveTarget != None) { if (ShouldPlayWalk(MoveTarget.Location)) PlayRunning(); MoveToward(MoveTarget, 1.0); Goto('Follow'); } Pause: Acceleration = vect(0, 0, 0); TurnToward(orderActor); PlayWaiting(); Sleep(1.0); Goto('Follow'); Done: if (HasNextState()) GotoNextState(); else GotoState('Standing'); ContinueRun: ContinueFromDoor: PlayRunning(); Goto('Follow'); } // ---------------------------------------------------------------------- // state Burning // // Whoa-oh-oh, I'm on fire. // ---------------------------------------------------------------------- state Burning { function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { local name newLabel; if (health > 0) { if (enemy != instigatedBy) { SetEnemy(instigatedBy); newLabel = 'NewEnemy'; } else newLabel = 'ContinueBurn'; if ( Enemy != None ) LastSeenPos = Enemy.Location; SetNextState('Burning', newLabel); if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned')) GotoDisabledState(damageType, hitPos); } } function SetFall() { StartFalling('Burning', 'ContinueBurn'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function PickDestination() { local float magnitude; local float distribution; local int yaw, pitch; local Rotator rotator1; local NavigationPoint nav; local float dist; local NavigationPoint bestNav; local float bestDist; destPoint = None; bestNav = None; bestDist = 2000; // max distance to water // Seek out water if (bCanSwim) { nav = Level.NavigationPointList; while (nav != None) { if (nav.Region.Zone.bWaterZone) { dist = VSize(Location - nav.Location); if (dist < bestDist) { bestNav = nav; bestDist = dist; } } nav = nav.nextNavigationPoint; } } if (bestNav != None) { // It'd be nice if we could traverse all pathnodes and figure out their // distances... unfortunately, it's too slow. :( MoveTarget = FindPathToward(bestNav); if (MoveTarget != None) { destPoint = bestNav; destLoc = bestNav.Location; } } // Can't get to water -- run willy-nilly if (destPoint == None) { if (Enemy == None) { yaw = 0; pitch = 0; distribution = 0; } else { rotator1 = Rotator(Location-Enemy.Location); yaw = rotator1.Yaw; pitch = rotator1.Pitch; distribution = 0.5; } magnitude = 300*(FRand()*0.4+0.8); // 400, +/-20% if (!AIPickRandomDestination(100, magnitude, yaw, distribution, pitch, distribution, 4, FRand()*0.4+0.35, destLoc)) destLoc = Location+(VRand()*200); // we give up } } function BeginState() { StandUp(); BlockReactions(); bCanConverse = False; SetupWeapon(false, true); bStasis = False; SetDistress(true); EnemyLastSeen = 0; SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); ResetReactions(); bCanConverse = True; bStasis = True; } Begin: if (!bOnFire) Goto('Done'); PlayOnFireSound(); NewEnemy: Acceleration = vect(0, 0, 0); Run: if (!bOnFire) Goto('Done'); PlayPanicRunning(); PickDestination(); if (destPoint != None) { MoveToward(MoveTarget, MaxDesiredSpeed); while ((MoveTarget != None) && (MoveTarget != destPoint)) { MoveTarget = FindPathToward(destPoint); if (MoveTarget != None) MoveToward(MoveTarget, MaxDesiredSpeed); } } else MoveTo(destLoc, MaxDesiredSpeed); Goto('Run'); Done: if (IsValidEnemy(Enemy)) HandleEnemy(); else FollowOrders(); ContinueBurn: ContinueFromDoor: Goto('Run'); } // ---------------------------------------------------------------------- // state AvoidingProjectiles // // Run away from a projectile. // ---------------------------------------------------------------------- state AvoidingProjectiles { ignores EnemyNotVisible; function SetFall() { StartFalling('RunningTo', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function AnimEnd() { PlayWaiting(); } function PickDestination(bool bGotoWatch) { local NearbyProjectileList projList; local bool bMove; local vector projVector; local rotator projRot; local int i; local int bestSlot; local float bestDist; destLoc = vect(0,0,0); destPoint = None; bMove = false; if (GetProjectileList(projList, Location) > 0) { if (IsLocationDangerous(projList, Location)) { projVector = ComputeAwayVector(projList); projRot = Rotator(projVector); if (AIDirectionReachable(Location, projRot.Yaw, projRot.Pitch, CollisionRadius+24, VSize(projVector), destLoc)) { useLoc = Location + vect(0,0,1)*BaseEyeHeight; // hack bMove = true; } } } if (bMove) GotoState('AvoidingProjectiles', 'RunAway'); else if (bGotoWatch) GotoState('AvoidingProjectiles', 'Watch'); } function BeginState() { StandUp(); Disable('AnimEnd'); bCanJump = false; SetReactions(true, true, true, true, false, true, true, true, true, true, true, true); bStasis = False; useLoc = Location + vect(0,0,1)*BaseEyeHeight + Vector(Rotation); bCanConverse = False; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); if (JumpZ > 0) bCanJump = true; ResetReactions(); bStasis = True; bCanConverse = True; } Begin: Acceleration = vect(0,0,0); PickDestination(true); RunAway: PlayTurnHead(LOOK_Forward, 1.0, 0.0001); if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); PickDestination(true); Watch: Acceleration = vect(0,0,0); PlayWaiting(); LookAtVector(useLoc, true, false, true); TurnTo(Vector(DesiredRotation)*1000+Location); sleepTime = 3.0; while (sleepTime > 0) { sleepTime -= 0.5; Sleep(0.5); PickDestination(false); } Done: if (Orders != 'AvoidingProjectiles') FollowOrders(); else GotoState('Wandering'); ContinueRun: ContinueFromDoor: PickDestination(false); Goto('Done'); } // ---------------------------------------------------------------------- // state AvoidingPawn // // Run away from an onrushing pawn (used for small, dumb critters only). // ---------------------------------------------------------------------- state AvoidingPawn { ignores EnemyNotVisible; function SetFall() { StartFalling('AvoidingPawn', 'ContinueAvoid'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function PickDestination() { local int iterations; local float magnitude; local rotator rot; local float speed; local float time; local vector newPos; local float minDist; minDist = 20; speed = VSize(Enemy.Velocity); if (speed == 0) time = 1; else time = VSize(Location - Enemy.Location)/speed; newPos = Enemy.Location + Enemy.Velocity*(time*0.98); magnitude = 100*(FRand()*0.2+0.9); // 120, +/-10% rot = Rotator(Location-newPos); iterations = 2; if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) { rot = Rotator(Location - Enemy.Location); if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) { if (speed > 0) rot = Rotator(Enemy.Velocity); else rot = Enemy.Rotation; if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) { rot.Yaw = -rot.Yaw; rot.Pitch = -rot.Pitch; if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc)) destLoc = Location; // we give up } } } } function BeginState() { StandUp(); bCanJump = false; bStasis = False; SetupWeapon(false); SetDistress(false); SeekPawn = None; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; bStasis = True; } Begin: if (!ShouldBeStartled(Enemy)) Goto('Done'); Goto('Avoid'); ContinueFromDoor: Goto('Avoid'); Avoid: ContinueAvoid: if (!ShouldBeStartled(Enemy)) Goto('Done'); PickDestination(); if (destLoc == Location) Goto('Pause'); if (ShouldPlayWalk(destLoc)) PlayRunning(); MoveTo(destLoc, MaxDesiredSpeed); Goto('Avoid'); Pause: PlayWaiting(); Sleep(0.0); Goto('Avoid'); Done: if (Orders != 'AvoidingPawn') FollowOrders(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state BackingOff // // Hack state used to back off when the NPC gets stuck. // ---------------------------------------------------------------------- state BackingOff { ignores EnemyNotVisible; function SetFall() { StartFalling('BackingOff', 'ContinueRun'); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); CheckOpenDoor(HitNormal, Wall); } function bool PickDestination() { local bool bSuccess; local float magnitude; local rotator rot; magnitude = 300; rot = Rotator(Destination-Location); bSuccess = AIPickRandomDestination(64, magnitude, rot.Yaw+32768, 0.8, -rot.Pitch, 0.8, 3, 0.9, useLoc); return bSuccess; } function bool HandleTurn(Actor Other) { GotoState('BackingOff', 'Pause'); return false; } function BeginState() { StandUp(); BlockReactions(); bStasis = False; bInTransientState = True; EnableCheckDestLoc(false); bCanJump = false; } function EndState() { EnableCheckDestLoc(false); if (JumpZ > 0) bCanJump = true; ResetReactions(); bStasis = True; bInTransientState = false; } Begin: useRot = Rotation; if (!PickDestination()) Goto('Pause'); Acceleration = vect(0,0,0); MoveAway: if (ShouldPlayWalk(useLoc)) PlayRunning(); MoveTo(useLoc, MaxDesiredSpeed); Pause: Acceleration = vect(0,0,0); PlayWaiting(); Sleep(FRand()*2+2); Done: if (HasNextState()) GotoNextState(); else FollowOrders(); // THIS IS BAD!!! ContinueRun: ContinueFromDoor: Goto('Done'); } // ---------------------------------------------------------------------- // state OpeningDoor // // Open a door. // ---------------------------------------------------------------------- state OpeningDoor { ignores EnemyNotVisible; function SetFall() { StartFalling(NextState, NextLabel); } function HitWall(vector HitNormal, actor Wall) { if (Physics == PHYS_Falling) return; Global.HitWall(HitNormal, Wall); if (Target == Wall) CheckOpenDoor(HitNormal, Wall); } function bool DoorEncroaches() { local bool bEncroaches; local DeusExMover dxMover; bEncroaches = true; dxMover = DeusExMover(Target); if (dxMover != None) { if (IsDoor(dxMover) && (dxMover.MoverEncroachType == ME_IgnoreWhenEncroach)) bEncroaches = false; } return bEncroaches; } function FindBackupPoint() { local vector hitNorm; local rotator rot; local vector center; local vector area; local vector relPos; local float distX, distY; local float dist; hitNorm = Normal(destLoc); rot = Rotator(hitNorm); DeusExMover(Target).ComputeMovementArea(center, area); area.X += CollisionRadius + 30; area.Y += CollisionRadius + 30; //area.Z += CollisionHeight + 30; relPos = Location - center; if ((relPos.X < area.X) && (relPos.X > -area.X) && (relPos.Y < area.Y) && (relPos.Y > -area.Y)) { // hack if (hitNorm.Y == 0) hitNorm.Y = 0.00000001; if (hitNorm.X == 0) hitNorm.X = 0.00000001; if (hitNorm.X > 0) distX = (area.X - relPos.X)/hitNorm.X; else distX = (-area.X - relPos.X)/hitNorm.X; if (hitNorm.Y > 0) distY = (area.Y - relPos.Y)/hitNorm.Y; else distY = (-area.Y - relPos.Y)/hitNorm.Y; dist = FMin(distX, distY); if (dist < 45) dist = 45; else if (dist > 700) dist = 700; // sanity check if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 40, dist, destLoc)) destLoc = Location; } else destLoc = Location; } function vector FocusDirection() { return (Vector(Rotation)*30+Location); } function StopWaiting() { GotoState('OpeningDoor', 'DoorOpened'); } function AnimEnd() { local DeusExMover moved; moved = DeusExMover(Target); if (moved != none && moved.MoveTime <= 1.5) ResetReactions(); //CyberP: this resets reactions early for fast-moving doors PlayWaiting(); } function BeginState() { StandUp(); Disable('AnimEnd'); bCanJump = false; BlockReactions(); bStasis = False; bInTransientState = True; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bAcceptBump = True; if (JumpZ > 0) bCanJump = true; ResetReactions(); bStasis = True; bInTransientState = false; } Begin: destLoc = vect(0,0,0); BeginHitNormal: Acceleration = vect(0,0,0); FindBackupPoint(); if (!DoorEncroaches()) if (!FrobDoor(Target)) Goto('DoorOpened'); PlayRunning(); StrafeTo(destLoc, FocusDirection()); if (DoorEncroaches()) if (!FrobDoor(Target)) Goto('DoorOpened'); PlayWaiting(); if (target != None && target.IsA('DeusExMover') && DeusExMover(target).MoveTime < 5.0 && DeusExMover(target).bIsDoor) //CyberP: added this block, because Sleeping for 5 secs in combat is bad. { if (enemy != None && DeusExMover(target).MoverEncroachType == ME_IgnoreWhenEncroach) { Sleep(DeusExMover(target).MoveTime*0.4); FollowOrders(); } else Sleep(DeusExMover(target).MoveTime); } else Sleep(5.0); DoorOpened: if (HasNextState()) GotoNextState(); else FollowOrders(); // THIS IS BAD!!! } // ---------------------------------------------------------------------- // state TakingHit // // React to a hit. // ---------------------------------------------------------------------- state TakingHit { ignores seeplayer, hearnoise, bump, hitwall, reacttoinjury; function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false); } function Landed(vector HitNormal) { if (Velocity.Z < -1.4 * JumpZ) MakeNoise(-0.5 * Velocity.Z/(FMax(JumpZ, 150.0))); bJustLanded = true; } function BeginState() { local DeusExPlayer player; player = DeusExPlayer(GetPlayerPawn()); StandUp(); LastPainTime = Level.TimeSeconds; LastPainAnim = AnimSequence; bInterruptState = false; BlockReactions(); bCanConverse = False; bStasis = False; SetDistress(true); if (player.PerkNamesArray[14]==1 && player.inHand != None && DeusExWeapon(player.InHand).bHandToHand) TakeHitTimer = 1.3; else if (IsA('Gray')) //CyberP: grays are formidable TakeHitTimer = 10.0; else TakeHitTimer = 2.25; //CyberP: was 2.0 EnemyReadiness = 1.0; ReactionLevel = 1.0; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bInterruptState = true; ResetReactions(); bCanConverse = True; bStasis = True; bInTransientState = false; } Begin: Acceleration = vect(0, 0, 0); FinishAnim(); if ( (Physics == PHYS_Falling) && !Region.Zone.bWaterZone ) { Acceleration = vect(0,0,0); GotoState('FallingState', 'Ducking'); } else if (HasNextState()) GotoNextState(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state RubbingEyes // // React to evil things like pepper spray. // ---------------------------------------------------------------------- state RubbingEyes { ignores seeplayer, hearnoise, bump, hitwall; function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false); } function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned')) Global.ReactToInjury(instigatedBy, damageType, hitPos); } function SetFall() { StartFalling(NextState, NextLabel); } function AnimEnd() { PlayWaiting(); } function BeginState() { StandUp(); Disable('AnimEnd'); // LastPainTime = Level.TimeSeconds; // LastPainAnim = AnimSequence; bInterruptState = false; BlockReactions(); bCanConverse = False; bStasis = False; SetupWeapon(false, true); SetDistress(true); bStunned = True; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bInterruptState = true; ResetReactions(); bCanConverse = True; bStasis = True; if (Health > 0) bStunned = False; bInTransientState = false; } Begin: Acceleration = vect(0, 0, 0); PlayTearGasSound(); RubEyes: PlayRubbingEyesStart(); FinishAnim(); PlayRubbingEyes(); Sleep(15); PlayRubbingEyesEnd(); FinishAnim(); if (HasNextState()) GotoNextState(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state Stunned // // React to being stunned. // ---------------------------------------------------------------------- state Stunned { ignores seeplayer, hearnoise, bump, hitwall; function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType) { TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false); } function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos) { if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned')) Global.ReactToInjury(instigatedBy, damageType, hitPos); } function SetFall() { StartFalling(NextState, NextLabel); } function AnimEnd() { PlayWaiting(); } function BeginState() { StandUp(); Disable('AnimEnd'); bInterruptState = false; BlockReactions(); bCanConverse = False; bStasis = False; SetupWeapon(false); SetDistress(true); bStunned = True; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bInterruptState = true; ResetReactions(); bCanConverse = True; bStasis = True; // if we're dead, don't reset the flag if (Health > 0) bStunned = False; bInTransientState = false; } Begin: Acceleration = vect(0, 0, 0); PlayStunned(); Sleep(15); if (HasNextState()) GotoNextState(); else GotoState('Wandering'); } // ---------------------------------------------------------------------- // state Dying // // Why does the Unreal Dying state suck? // ---------------------------------------------------------------------- state Dying { ignores SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, Died, Timer, TakeDamage; event Landed(vector HitNormal) { SetPhysics(PHYS_Walking); } function Tick(float deltaSeconds) { Global.Tick(deltaSeconds); if (DeathTimer > 0) { DeathTimer -= deltaSeconds; if ((DeathTimer <= 0) && (Physics == PHYS_Walking)) Acceleration = vect(0,0,0); } } function MoveFallingBody() { local Vector moveDir; local float totalTime; local float speed; local float stopTime; local int numFrames; if ((AnimRate > 0) && !IsA('Robot')) { totalTime = 1.0/AnimRate; // determine how long the anim lasts numFrames = int((1.0/(1.0-AnimLast))+0.1); // count frames (hack) // defaults moveDir = vect(0,0,0); stopTime = 0.01; ComputeFallDirection(totalTime, numFrames, moveDir, stopTime); speed = VSize(moveDir)/stopTime; // compute speed // Set variables necessary for movement when walking if (moveDir == vect(0,0,0)) Acceleration = vect(0,0,0); else Acceleration = Normal(moveDir)*AccelRate; GroundSpeed = speed; DesiredSpeed = 1.0; bIsWalking = false; DeathTimer = stopTime; } else Acceleration = vect(0,0,0); } function BeginState() { EnableCheckDestLoc(false); StandUp(); if (Weapon != None && Weapon.IsA('WeaponRifle')) //CyberP: hack: set back rifle stats { WeaponRifle(Weapon).BaseAccuracy = WeaponRifle(Weapon).default.BaseAccuracy; WeaponRifle(Weapon).maxRange = WeaponRifle(Weapon).default.MaxRange; WeaponRifle(Weapon).AccurateRange = WeaponRifle(Weapon).default.AccurateRange; WeaponRifle(Weapon).ShotTime = WeaponRifle(Weapon).default.ShotTime; } // don't do that stupid timer thing in Pawn.uc AIClearEventCallback('Futz'); AIClearEventCallback('MegaFutz'); AIClearEventCallback('Player'); AIClearEventCallback('WeaponDrawn'); AIClearEventCallback('LoudNoise'); AIClearEventCallback('WeaponFire'); AIClearEventCallback('Carcass'); AIClearEventCallback('Distress'); AIClearEventCallback('Projetile'); bInterruptState = false; BlockReactions(true); bCanConverse = False; bStasis = False; SetDistress(true); DeathTimer = 0; } Begin: WaitForLanding(); MoveFallingBody(); DesiredRotation.Pitch = 0; DesiredRotation.Roll = 0; // if we don't gib, then wait for the animation to finish if ((Health > -100) && !IsA('Robot')) { //if (!IsA('Animal')) // SetCollisionSize(0.001000,Default.CollisionHeight); //CyberP: massive hack. May have to remove (all in this block and then uncomment finishAnim()) FinishAnim(); //if (!IsA('Animal')) // SetCollisionSize(Default.CollisionRadius,Default.CollisionHeight); } //FinishAnim(); SetWeapon(None); bHidden = True; Acceleration = vect(0,0,0); SpawnCarcass(); Destroy(); } // ---------------------------------------------------------------------- // state FallingState // // Fall! // ---------------------------------------------------------------------- state FallingState { ignores Bump, Hitwall, WarnTarget, ReactToInjury; function ZoneChange(ZoneInfo newZone) { Global.ZoneChange(newZone); if (newZone.bWaterZone) GotoState('FallingState', 'Splash'); } //choose a jump velocity function AdjustJump() { local float velZ; local vector FullVel; velZ = Velocity.Z; FullVel = Normal(Velocity) * GroundSpeed; If (Location.Z > Destination.Z + CollisionHeight + 2 * MaxStepHeight) { Velocity = FullVel; Velocity.Z = velZ; Velocity = EAdjustJump(); Velocity.Z = 0; if ( VSize(Velocity) < 0.9 * GroundSpeed ) { Velocity.Z = velZ; return; } } Velocity = FullVel; Velocity.Z = JumpZ + velZ; Velocity = EAdjustJump(); } singular function BaseChange() { local float minJumpZ; Global.BaseChange(); if (Physics == PHYS_Walking) { minJumpZ = FMax(JumpZ, 150.0); bJustLanded = true; if (Health > 0) { if ((Velocity.Z < -0.8 * minJumpZ) || bUpAndOut) GotoState('FallingState', 'Landed'); else if (Velocity.Z < -0.8 * JumpZ) GotoState('FallingState', 'FastLanded'); else GotoState('FallingState', 'Done'); } } } function Landed(vector HitNormal) { local float landVol, minJumpZ; local vector legLocation; minJumpZ = FMax(JumpZ, 150.0); if ( (Velocity.Z < -0.8 * minJumpZ) || bUpAndOut) { PlayLanded(Velocity.Z); if (Velocity.Z < -700) { legLocation = Location + vect(-1,0,-1); // damage left leg TakeDamage(-0.14 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell'); legLocation = Location + vect(1,0,-1); // damage right leg TakeDamage(-0.14 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell'); legLocation = Location + vect(0,0,1); // damage torso TakeDamage(-0.04 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell'); } landVol = Velocity.Z/JumpZ; landVol = 0.005 * Mass * FMin(5, landVol * landVol); if ( !FootRegion.Zone.bWaterZone ) { if(DeusExPlayer(GetPlayerPawn()).DrugEffectTimer < 0) PlaySound(Land, SLOT_Interact, FMin(20, landVol),,,0.5); else PlaySound(Land, SLOT_Interact, FMin(20, landVol)); } } else if ( Velocity.Z < -0.8 * JumpZ ) PlayLanded(Velocity.Z); } function SetFall() { if (!bUpAndOut) GotoState('FallingState'); } function BeginState() { StandUp(); if (Enemy == None) Disable('EnemyNotVisible'); else { Disable('HearNoise'); Disable('SeePlayer'); } bInterruptState = false; bCanConverse = False; bStasis = False; bInTransientState = true; EnableCheckDestLoc(false); } function EndState() { EnableCheckDestLoc(false); bUpAndOut = false; bInterruptState = true; bCanConverse = True; bStasis = True; bInTransientState = false; } LongFall: if ( bCanFly ) { SetPhysics(PHYS_Flying); Goto('Done'); } Sleep(0.7); PlayFalling(); if ( Velocity.Z > -150 ) //stuck { SetPhysics(PHYS_Falling); if ( Enemy != None ) Velocity = groundspeed * normal(Enemy.Location - Location); else Velocity = groundspeed * VRand(); Velocity.Z = FMax(JumpZ, 250); } Goto('LongFall'); FastLanded: FinishAnim(); TweenToWaiting(0.15); Goto('Done'); Landed: if ( !bIsPlayer ) //bots act like players Acceleration = vect(0,0,0); FinishAnim(); TweenToWaiting(0.2); if ( !bIsPlayer ) Sleep(0.08); Done: bUpAndOut = false; if (HasNextState()) GotoNextState(); else GotoState('Wandering'); Splash: bUpAndOut = false; FinishAnim(); if (HasNextState()) GotoNextState(); else GotoState('Wandering'); Begin: if (Enemy == None) Disable('EnemyNotVisible'); else { Disable('HearNoise'); Disable('SeePlayer'); } if (bUpAndOut) //water jump { if ( !bIsPlayer ) { DesiredRotation = Rotation; DesiredRotation.Pitch = 0; Velocity.Z = 440; } } else { if (Region.Zone.bWaterZone) { SetPhysics(PHYS_Swimming); GotoNextState(); } if ( !bJumpOffPawn ) AdjustJump(); else bJumpOffPawn = false; PlayFall: PlayFalling(); FinishAnim(); } if (Physics != PHYS_Falling) Goto('Done'); Sleep(2.0); Goto('LongFall'); Ducking: } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // OBSOLETE // ---------------------------------------------------------------------- function PlayHit(float Damage, vector HitLocation, name damageType, vector Momentum) { log("ERROR - PlayHit should not be called!"); } function PlayHitAnim(vector HitLocation, float Damage) { log("ERROR - PlayHitAnim should not be called!"); } function PlayDeathHit(float Damage, vector HitLocation, name damageType, vector Momentum) { log("ERROR - PlayDeathHit should not be called!"); } function PlayChallenge() { log("ERROR - PlayChallenge should not be called!"); } function JumpOffPawn() { /* Velocity += (60 + CollisionRadius) * VRand(); Velocity.Z = 180 + CollisionHeight; SetPhysics(PHYS_Falling); bJumpOffPawn = true; SetFall(); */ //log("ERROR - JumpOffPawn should not be called!"); } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- defaultproperties { Restlessness=0.500000 Wanderlust=0.500000 Cowardice=0.500000 maxRange=4000.000000 MinHealth=30.000000 RandomWandering=1.000000 bAlliancesChanged=True Orders=Wandering HomeExtent=800.000000 WalkingSpeed=0.450000 bCanBleed=True ClotPeriod=30.000000 bShowPain=True bCanSit=True bLikesNeutral=True bUseFirstSeatOnly=True bCanConverse=True bAvoidAim=True AvoidAccuracy=0.400000 bAvoidHarm=True HarmAccuracy=0.700000 CloseCombatMult=0.300000 bHateShot=True bHateInjury=True bReactPresence=True bReactProjectiles=True bEmitDistress=True RaiseAlarm=RAISEALARM_BeforeFleeing bMustFaceTarget=True FireAngle=360.000000 MaxProvocations=1 AgitationSustainTime=30.000000 AgitationDecayRate=0.050000 FearSustainTime=25.000000 FearDecayRate=0.500000 SurprisePeriod=1.000000 SightPercentage=0.500000 bHasShadow=True ShadowScale=1.000000 BaseAssHeight=-26.000000 EnemyTimeout=4.000000 bTickVisibleOnly=True bInWorld=True bHighlight=True bHokeyPokey=True InitialInventory(0)=(Count=1) InitialInventory(1)=(Count=1) InitialInventory(2)=(Count=1) InitialInventory(3)=(Count=1) InitialInventory(4)=(Count=1) InitialInventory(5)=(Count=1) InitialInventory(6)=(Count=1) InitialInventory(7)=(Count=1) bSpawnBubbles=True bWalkAround=True BurnPeriod=40.000000 DistressTimer=-1.000000 CloakThreshold=50 walkAnimMult=0.700000 runAnimMult=1.050000 bRadialBlastClamp=True bCanStrafe=True bCanWalk=True bCanSwim=True bCanOpenDoors=True bIsHuman=True bCanGlide=False AirSpeed=320.000000 AccelRate=200.000000 JumpZ=120.000000 MinHitWall=9999999827968.000000 HearingThreshold=0.150000 Skill=2.000000 AIHorizontalFov=160.000000 AspectRatio=2.300000 bForceStasis=True CollisionRadius=21.000000 BindName="ScriptedPawn" FamiliarName="DEFAULT FAMILIAR NAME - REPORT THIS AS A BUG" UnfamiliarName="DEFAULT UNFAMILIAR NAME - REPORT THIS AS A BUG" }
違いを見つける