2023-03-28 13:16:30 -04:00
using System.Collections ;
using System.Collections.Generic ;
using UnityEngine ;
using RPGCreationKit ;
using RPGCreationKit.BehaviourTree ;
using UnityEngine.AI ;
using RPGCreationKit.SaveSystem ;
using RPGCreationKit.CellsSystem ;
using RPGCreationKit.Player ;
using System ;
namespace RPGCreationKit.AI
{
2023-04-26 01:28:58 -04:00
[DisallowMultipleComponent]
public class RckAI : AIBehaviourTreed
{
public Race race ;
public bool hasBeenInstantiated ;
private void Reset ( )
{
iGUIDReferences = GetComponent < AIGUIDReferences > ( ) ;
m_Anim = GetComponent < Animator > ( ) ;
attributes = GetComponent < EntityAttributes > ( ) ;
ragdoll = GetComponentInChildren < Ragdoll > ( ) ;
aiLookAt = GetComponent < AILookAt > ( ) ;
headPos = GetComponentInChildren < AIHeadPos > ( ) . transform ;
entityFocusPart = headPos ;
physicalCollider = GetComponent < Collider > ( ) ;
interactableCollider = RCKFunctions . GetChildWithName ( this . gameObject , "InteractZone" ) . GetComponent < Collider > ( ) ;
agent = GetComponent < NavMeshAgent > ( ) ;
m_Rigidbody = GetComponent < Rigidbody > ( ) ;
movementType = MovementType . UnityNavmesh ;
arriveSteeringBehaviourDeceleration = Deceleration . Normal ;
inventory = GetComponent < Inventory > ( ) ;
equipment = GetComponent < Equipment > ( ) ;
bodyData = GetComponent < BodyData > ( ) ;
defaultWeaponOnHand = RCKFunctions . GetChildWithName ( this . gameObject , "DefaultWeapon" ) . GetComponent < WeaponOnHand > ( ) ;
defaultWeapon = defaultWeaponOnHand . weaponItem ;
lootingPoint = GetComponentInChildren < LootingPoint > ( ) ;
lootingPoint . inventory = inventory ;
lootingPoint . equipment = equipment ;
onlineComponents . ai = GetComponent < RckAI > ( ) ;
onlineComponents . GFX = RCKFunctions . GetChildWithName ( this . gameObject , "GFX" ) ;
onlineComponents . rigidbody = m_Rigidbody ;
onlineComponents . animator = m_Anim ;
onlineComponents . iGUIDReferences = iGUIDReferences ;
onlineComponents . agent = agent ;
onlineComponents . audioSource = GetComponent < AudioSource > ( ) ;
2023-03-28 13:16:30 -04:00
2023-04-26 01:28:58 -04:00
}
public void DelayedStart ( )
{
base . Start ( ) ;
// Ignore children colliders
CapsuleCollider coll = GetComponent < CapsuleCollider > ( ) ;
var colliders = GetComponentsInChildren < Collider > ( ) ;
for ( int i = 0 ; i < colliders . Length ; i + + )
Physics . IgnoreCollision ( coll , colliders [ i ] ) ;
}
public IEnumerator QueueSet ( bool _combat , string _treeID )
{
if ( anyWaitForSwitchRunning )
yield return null ;
SetNewBehaviourTree ( _combat , _treeID ) ;
}
[AIInvokable]
public void SetNewBehaviourTree ( bool _combat , string _treeID )
{
if ( anyWaitForSetRunning )
{
StartCoroutine ( QueueSet ( _combat , _treeID ) ) ;
return ;
}
// Execute OnExit node if present
if ( currentBehaviour ! = null & & currentBehaviour . nodes . Count > 0 & & ( ( currentBehaviour . nodes [ 0 ] as BTNode ) . GetOutputPort ( "onExitNode" ) . Connection ! = null & & ( ( currentBehaviour . nodes [ 0 ] as BTNode ) . GetOutputPort ( "onExitNode" ) . Connection . node ! = null ) ) )
{
anyWaitForSetRunning = true ;
// Execute OnExit before changing the tree
StartCoroutine ( SetNewBehaviourTree_WaitForExitNodeTask ( _combat , _treeID ) ) ;
}
else
{
RPGCK_BT newBT = BehaviourDatabase . GetItem ( _treeID ) . RPGCK_BTCopy ( gameObject , this ) ;
if ( _combat )
combatBehaviourTree = newBT ;
else
purposeBehaviourTree = newBT ;
}
}
IEnumerator SetNewBehaviourTree_WaitForExitNodeTask ( bool _combat , string _treeID )
{
pauseBT = true ;
yield return new WaitForEndOfFrame ( ) ;
var node = ( ( ( currentBehaviour . nodes [ 0 ] as BTNode ) . GetOutputPort ( "onExitNode" ) . Connection . node ) as BTNode ) ;
node . ReEvaluate ( ) ;
while ( node . m_NodeState = = NodeState . Null | | node . m_NodeState = = NodeState . Running )
{
node . Execute ( ) ;
yield return new WaitForEndOfFrame ( ) ;
}
RPGCK_BT newBT = BehaviourDatabase . GetItem ( _treeID ) . RPGCK_BTCopy ( gameObject , this ) ;
if ( _combat )
combatBehaviourTree = newBT ;
else
purposeBehaviourTree = newBT ;
anyWaitForSetRunning = false ;
pauseBT = false ;
yield break ;
}
[AIInvokable]
public void SetNewBehaviourTreeToAI ( string _toAI , bool _combat , string _treeID )
{
// Get the RckAI if possible
RckAI sTo = null ;
CellsSystem . CellInformation . TryToGetAI ( _toAI , out sTo ) ;
if ( sTo ! = null )
{
sTo . SetNewBehaviourTree ( _combat , _treeID ) ;
}
}
public bool TryLoadFromSavefile ( )
{
GetMyCellInfoImmediate ( ) ;
var allAI = SaveSystemManager . instance . saveFile . AIData . aiDictionary ;
if ( allAI . ContainsKey ( entityID ) )
{
// Look if it's in the same original cell
if ( startingCellID = = allAI [ entityID ] . saveCellID | | hasBeenInstantiated )
{
// Load it
StartCoroutine ( LoadAI ( allAI [ entityID ] ) ) ;
}
else
{
CellInformation . activeCells [ myCellInfo . cell . ID ] . aiInWorld . Remove ( entityID ) ;
// Destroy this AI, it will be instantiated by CellInfo
Destroy ( this . gameObject ) ;
}
}
else
{
pauseBT = true ;
StartCoroutine ( NewAIWaitsForLoad ( ) ) ;
return true ;
}
return true ;
}
public IEnumerator NewAIWaitsForLoad ( )
{
isLoaded = true ;
// Wait for game to start before letting the ai do its job
while ( ! CellInformation . AllActiveCellsLoaded ( ) | | WorldManager . instance . isLoading )
yield return null ;
GetMyCellInfoImmediate ( ) ;
pauseBT = false ;
}
public IEnumerator LoadAI ( AISaveData data )
{
yield return new WaitForEndOfFrame ( ) ;
pauseBT = true ;
yield return new WaitForEndOfFrame ( ) ;
agent . enabled = false ;
startingCellID = data . startingCellID ;
transform . position = data . position ;
transform . rotation = data . rotation ;
// Load Entity Attributes
attributes . attributes = data . aiAttributes . attributes ;
attributes . derivedAttributes = data . aiAttributes . derivedAttributes ;
attributes . ExecuteEffects ( data . aiAttributes . activeEffects . ToArray ( ) ) ;
ResetRecover ( ) ;
inventory . ClearInventoryAndEquipment ( ) ;
equipment . OnEquipmentChanges ( ) ;
OnEquipmentChangesHands ( ) ;
OnEquipmentChangesAmmo ( ) ;
// Those items gets equipped after everyone else, used for the shield
List < ItemInInventory > toEquipAfter = new List < ItemInInventory > ( ) ;
for ( int i = 0 ; i < data . aiInventory . items . Count ; i + + )
{
var item = data . aiInventory . items [ i ] ;
var aItem = inventory . AddItem ( item . externalItemID , item . Amount ) ;
aItem . metadata . IsOwnedByNPC = true ;
if ( item . isEquipped )
{
if ( aItem . item . itemType = = ItemTypes . ArmorItem & & RCKFunctions . ContainsBiped ( ( ( ArmorItem ) ( aItem . item ) ) . Bipeds , BipedObject . Shield ) )
{
// Delay the equipment of shields
toEquipAfter . Add ( aItem ) ;
}
else
equipment . Equip ( aItem ) ;
}
}
equipment . OnEquipmentChanges ( ) ;
OnEquipmentChangesHands ( ) ;
OnEquipmentChangesAmmo ( ) ;
// Equip delayed
for ( int i = 0 ; i < toEquipAfter . Count ; i + + )
{
equipment . Equip ( toEquipAfter [ i ] ) ;
}
inventory . InitializeInventory ( ) ;
GetMyCellInfoImmediate ( ) ;
// Load important states
isEssential = data . isEssential ;
if ( ! data . isAlive )
{
Die ( ) ;
if ( data . usesRagdoll )
{
ragdoll . ForceRagdoll ( ) ;
// Load hips pos/rot
ragdoll . LoadFromSaveData ( data . ragdollSaveData ) ;
}
}
else // Entity is alive
{
followTargetOutsideOfCell = data . followTargetOutsideOfCell ;
// Load Behaviour Trees
if ( ! string . IsNullOrEmpty ( data . purposeBehaviourTreeID ) )
purposeBehaviourTree = BehaviourDatabase . GetItem ( data . purposeBehaviourTreeID ) . RPGCK_BTCopy ( this . gameObject , this ) ;
if ( ! string . IsNullOrEmpty ( data . combatBehaviourTreeID ) )
combatBehaviourTree = BehaviourDatabase . GetItem ( data . combatBehaviourTreeID ) . RPGCK_BTCopy ( this . gameObject , this ) ;
SwitchBehaviourTree ( ! data . isUsingPurposeBehaviour ) ;
// Movements
lookAtVector3IfStopped = data . lookAtVector3IfStopped ;
vector3ToLookAtIfStopped = data . vector3ToLookAtIfStopped ;
// Load perception
radius = data . radius ;
sphereYOffset = data . sphereYoffset ;
sphereForwardOffset = data . sphereZoffset ;
// Load Combat state nad entities fighting
if ( data . isInCombat | | data . enterInCombatCalled )
{
for ( int i = 0 ; i < data . entityHesFighting . Count ; i + + )
{
if ( data . entityHesFighting [ i ] = = "PLAYER_ID" )
{
// Add the player
enemyTargets . Add ( new VisibleEnemy ( RckPlayer . instance , RckPlayer . instance . playerAttributes , new AggroInfo ( 50 ) ) ) ;
}
else
{
RckAI ai = null ;
CellInformation . activeCells [ myCellInfo . cell . ID ] . aiInWorld . TryGetValue ( data . entityHesFighting [ i ] , out ai ) ;
if ( ai ! = null )
enemyTargets . Add ( new VisibleEnemy ( ai , ai . GetComponent < EntityAttributes > ( ) , new AggroInfo ( 50 ) ) ) ;
}
}
if ( enemyTargets . Count > 0 )
EnterInCombatAgainst ( enemyTargets [ 0 ] . m_entity ) ;
}
// Load Dialogues
if ( ! string . IsNullOrEmpty ( data . currentDialogueID ) )
currentDialogueGraph = DialoguesDatabase . GetItem ( data . currentDialogueID ) ;
if ( ! string . IsNullOrEmpty ( data . previousDialogueID ) )
previousDialogueGraph = DialoguesDatabase . GetItem ( data . previousDialogueID ) ;
if ( ! string . IsNullOrEmpty ( data . defaultDialogueID ) )
defaultDialogueGraph = DialoguesDatabase . GetItem ( data . defaultDialogueID ) ;
dialogueSystemEnabled = data . dialogueSystemEnabled ;
// Load MainTarget
if ( ! string . IsNullOrEmpty ( data . mainTargetID ) )
{
ITargetableType targetableType = ( ITargetableType ) data . mainTargetType ;
switch ( targetableType )
{
case ITargetableType . Entity :
if ( data . mainTargetID = = "PLAYER_ID" )
SetTarget ( RckPlayer . instance . gameObject ) ;
else
{
RckAI entity = null ;
CellInformation . TryToGetAI ( data . mainTargetID , out entity ) ;
if ( entity ! = null )
SetTarget ( entity . gameObject ) ;
}
break ;
}
}
if ( ! string . IsNullOrEmpty ( data . cellIDMovingTo ) )
{
MoveToCell ( data . cellIDMovingTo ) ;
}
// Load PurposeState
purposeState = data . purposeState ;
// Try to solve purpose ref
if ( ! string . IsNullOrEmpty ( purposeState . ITargetableID ) )
{
ITargetableType targetableType = ( ITargetableType ) data . purposeState . ITargetableType ;
switch ( targetableType )
{
case ITargetableType . Entity :
if ( data . mainTargetID = = "PLAYER_ID" )
purposeState . AssignPurpose ( this , RckPlayer . instance . gameObject , data . purposeState . clearsOn , data . purposeState . clearsOnData , data . purposeState . nextPurposeBehaviourID ) ;
else
{
RckAI entity = null ;
CellInformation . TryToGetAI ( data . mainTargetID , out entity ) ;
if ( entity ! = null )
purposeState . AssignPurpose ( this , entity . gameObject , data . purposeState . clearsOn , data . purposeState . clearsOnData , data . purposeState . nextPurposeBehaviourID ) ;
}
break ;
case ITargetableType . Door :
purposeState . AssignPurpose ( this , GetTransformToDoor ( purposeState . ITargetableExtraData ) . gameObject , data . purposeState . clearsOn , data . purposeState . clearsOnData , data . purposeState . nextPurposeBehaviourID ) ;
break ;
case ITargetableType . PRef :
purposeState . AssignPurpose ( this , PersistentReferences . PersistentReferenceManager . instance . refs [ purposeState . ITargetableID ] . gameObject , data . purposeState . clearsOn , data . purposeState . clearsOnData , data . purposeState . nextPurposeBehaviourID ) ;
break ;
case ITargetableType . Transform :
if ( myCellInfo . allTargetables . ContainsKey ( purposeState . ITargetableID ) )
purposeState . AssignPurpose ( this , myCellInfo . allTargetables [ purposeState . ITargetableID ] . gameObject , data . purposeState . clearsOn , data . purposeState . clearsOnData , data . purposeState . nextPurposeBehaviourID ) ;
break ;
case ITargetableType . AIPath :
if ( myCellInfo . allAIPaths . ContainsKey ( purposeState . ITargetableID ) )
purposeState . AssignPurpose ( this , myCellInfo . allAIPaths [ purposeState . ITargetableID ] . gameObject , data . purposeState . clearsOn , data . purposeState . clearsOnData , data . purposeState . nextPurposeBehaviourID ) ;
break ;
}
if ( data . isUsingPurposeBehaviour )
purposeState . ResumePurpose ( ) ;
}
else
purposeState . ClearPurpose ( ) ;
// Move to door position if he entered a door
if ( data . enteredInADoorLastTime )
{
if ( ! data . enteredInADoorLastTimeOverridePos )
{
if ( ! string . IsNullOrEmpty ( data . doorEnteredID ) )
{
// TODO If he entered < 5 minutes ago bring him to the teleport mark
Door door = CellInformation . activeCells [ myCellInfo . cell . ID ] . allDoors [ data . doorEnteredID ] ;
transform . position = door . teleportMarker . position ;
// Otherwise if he entered > 5 minutes ago randomize his position
}
else
{
// just telport him to the cell entry
transform . position = myCellInfo . cellEntry . transform . position ;
transform . rotation = myCellInfo . cellEntry . transform . rotation ;
}
}
else
{
transform . position = data . enteredDoorLastTimePos ;
transform . rotation = data . enteredDoorLastTimeRot ;
// Reset boolean
enteredInADoorLastTime = false ;
}
}
if ( data . enteredInADoorLastTimeOverridePos )
{
enteredInADoorLastTimeOverridePos = data . enteredInADoorLastTimeOverridePos ;
enteredDoorLastTimePos = data . enteredDoorLastTimePos ;
enteredDoorLastTimeRot = data . enteredDoorLastTimeRot ;
}
if ( data . isFollowingAIPath )
{
shouldFollowAIPath = true ;
aiPathCurrentIndex = data . aiPathCurrentIndex ;
aiPathResumeFromCurrentIndex = true ;
invertAIPath = data . invertAIPath ;
FollowCurrentPath ( ) ;
}
SetOverrideShouldFollowMainTarget ( data . overrideShouldFollowMainTargetActive , data . overrideShouldFollowMainTarget ) ;
// Load factions
for ( int i = 0 ; i < data . allFactions . Count ; i + + )
AddToFaction ( data . allFactions [ i ] ) ;
}
// Assign runtime cell
runtimeStartingCell = myCellInfo . cell . ID ;
agent . enabled = true ;
isLoaded = true ;
OnChangeHairColor ( bodyData . HairColor ) ;
OnChangeSkinColor ( bodyData . SkinColor ) ;
OnChangeLipsColor ( bodyData . LipsColor ) ;
// Wait for game to start before letting the ai do its job
while ( ! CellInformation . AllActiveCellsLoaded ( ) | | WorldManager . instance . isLoading )
yield return null ;
pauseBT = false ;
yield return null ;
}
/// <summary>
/// THis method is called when the AI is being unloaded because the cell is too far away from the Player
/// </summary>
public void OnBeingUnloaded ( CellInformation calledBy )
{
if ( calledBy = = myCellInfo ) // if the ai is in the cell that is being unloaded
{
if ( teleportIfUnloadedWhileGoingToDoor )
{
if ( isReachingDoor )
{
isReachingDoor = false ;
enteredInADoorLastTime = true ;
TeleportToCell ( IsReachingCellID ) ;
}
}
}
}
public void SaveOnFile ( )
{
if ( isInOfflineMode ) // AI shouldn't be saved in offline mode
return ;
GetMyCellInfoImmediate ( ) ;
string saveCellID = myCellInfo . cell . ID ;
if ( ! string . IsNullOrEmpty ( cellIDOfLastSaved ) & & cellIDOfLastSaved ! = saveCellID )
{
// remove cellidoflastsaved
var dict = SaveSystemManager . instance . saveFile . AIData . aiCellDictionary ;
if ( dict . ContainsKey ( cellIDOfLastSaved ) )
dict [ cellIDOfLastSaved ] . allIDs . Remove ( entityID ) ;
}
cellIDOfLastSaved = saveCellID ;
var allAI = SaveSystemManager . instance . saveFile . AIData . aiDictionary ;
// Save this AI to the allAI dictionary
if ( allAI . ContainsKey ( entityID ) )
{
// Update entry
allAI [ entityID ] = ToSaveData ( ) ;
}
else // create
allAI . Add ( entityID , ToSaveData ( ) ) ;
// Save/Update/DoNothing on the aiCellDictionary
var allCellAI = SaveSystemManager . instance . saveFile . AIData . aiCellDictionary ;
// If the AI was loaded in a different cell from the starting point
if ( runtimeStartingCell ! = saveCellID )
{
if ( allCellAI . ContainsKey ( runtimeStartingCell ) )
{
if ( allCellAI [ runtimeStartingCell ] . allIDs . Contains ( entityID ) )
allCellAI [ runtimeStartingCell ] . allIDs . Remove ( entityID ) ;
}
// Add to the new one
if ( ! allCellAI . ContainsKey ( saveCellID ) )
{
allCellAI . Add ( saveCellID , new AIIDList ( ) ) ;
allCellAI [ saveCellID ] . allIDs . Add ( entityID ) ;
}
else
{
if ( ! allCellAI [ saveCellID ] . allIDs . Contains ( entityID ) )
allCellAI [ saveCellID ] . allIDs . Add ( entityID ) ;
}
transform . SetParent ( CellInformation . activeCells [ saveCellID ] . aiContainer ) ;
if ( ! CellInformation . activeCells [ saveCellID ] . aiInWorld . ContainsKey ( entityID ) )
CellInformation . activeCells [ saveCellID ] . aiInWorld . Add ( entityID , this ) ;
}
else
{
if ( ! allCellAI . ContainsKey ( runtimeStartingCell ) )
{
allCellAI . Add ( runtimeStartingCell , new AIIDList ( ) ) ;
allCellAI [ runtimeStartingCell ] . allIDs . Add ( entityID ) ;
}
}
}
public void SaveOnFile_JustInstantaited ( )
{
var allAI = SaveSystemManager . instance . saveFile . AIData . aiDictionary ;
// Save this AI to the allAI dictionary
if ( allAI . ContainsKey ( entityID ) )
{
// Update entry
allAI [ entityID ] = ToSaveData ( ) ;
}
else // create
allAI . Add ( entityID , ToSaveData ( ) ) ;
// Update CellAI dictionary
var aiCell = SaveSystem . SaveSystemManager . instance . saveFile . AIData . aiCellDictionary ;
if ( aiCell . ContainsKey ( cellIDOfLastSaved ) )
{
// Update it
var res = aiCell [ cellIDOfLastSaved ] ;
if ( ! res . allIDs . Contains ( entityID ) )
{
res . allIDs . Add ( entityID ) ;
aiCell [ cellIDOfLastSaved ] = res ;
}
}
else
{
AIIDList list = new AIIDList ( ) ;
list . allIDs . Add ( entityID ) ;
// Create new entry
aiCell . Add ( cellIDOfLastSaved , list ) ;
}
}
public AISaveData ToSaveData ( )
{
AISaveData newData = new AISaveData ( )
{
position = transform . position ,
rotation = transform . rotation ,
aiAttributes = attributes . ToSaveData ( ) ,
aiInventory = inventory . ToSaveData ( ) ,
isAlive = isAlive ,
isHostile = isHostile ,
isHostileAgainstPC = isHostileAgainstPC ,
isInCombat = isInCombat ,
isFleeing = isFleeing ,
isUnconscious = isUnconscious ,
enterInCombatCalled = enterInCombatCalled ,
isEssential = isEssential ,
usesRagdoll = usesRagdoll ,
dialogueSystemEnabled = dialogueSystemEnabled ,
currentDialogueID = ( currentDialogueGraph ) ? currentDialogueGraph . ID : string . Empty ,
previousDialogueID = ( previousDialogueGraph ) ? previousDialogueGraph . ID : string . Empty ,
defaultDialogueID = ( defaultDialogueGraph ) ? defaultDialogueGraph . ID : string . Empty ,
isInConversation = isInConversation ,
isTalking = isTalking ,
isListening = isListening ,
mustSpeakToPlayer = mustSpeakToPlayer ,
mustSpeakToEntity = mustSpeakToEntity ,
speakerIndex = speakerIndex ,
usesMovements = usesMovements ,
usesOfflineMode = usesOfflineMode ,
usesTlinkUnstuck = usesTlinkUnstuck ,
isWalking = isWalking ,
canSeeTarget = canSeeTarget ,
hasCompletlyLostTarget = hasCompletlyLostTarget ,
shouldFollowMainTarget = shouldFollowMainTarget ,
shouldFollowOnlyIfCanSee = shouldFollowOnlyIfCanSee ,
lookAtTarget = lookAtTarget ,
dialogueLookAt = dialogueLookAt ,
shouldFollowAIPath = shouldFollowAIPath ,
shouldWander = shouldWander ,
shouldUseNPCActionPoint = shouldUseNPCActionPoint ,
hasMainTarget = hasMainTarget ,
shouldFollowTargetVector = shouldFollowTargetVector ,
//aiCustomPathID = aiPath.ID,
//actionPointID = actionPoint.ID,
fleeAtDistanceValue = fleeAtDistanceValue ,
selectedSteeringBehaviour = ( int ) selectedSteeringBehaviour ,
arriveSteeringBehaviourDeceleration = ( int ) arriveSteeringBehaviourDeceleration ,
invertAIPath = invertAIPath ,
aiPathCurrentIndex = aiPathCurrentIndex ,
isFollowingAIPath = isFollowingAIPath ,
stoppingHalt = stoppingHalt ,
stoppingDistance = stoppingDistance ,
generalRotationSpeed = generalRotationSpeed ,
cachedMainTarget = cachedMainTarget ,
cachedMainTargetPos = cachedMainTargetPos ,
hasToRandomlySearchTarget = hasToRandomlySearchTarget ,
isReachingActionPoint = isReachingActionPoint ,
fixingInPlaceForActionPoint = fixingInPlaceForActionPoint ,
playingEnterAnimationActionPoint = playingEnterAnimationActionPoint ,
isUsingActionPoint = isUsingActionPoint ,
allowsLootWhenDead = allowsLootWhenDead ,
allowsLootOfEquipment = allowsLootOfEquipment ,
perceptionEnabled = perceptionEnabled ,
checkTick = checkTick ,
isDrawingWeapon = isDrawingWeapon ,
weaponDrawn = weaponDrawn ,
treeTickRate = ( int ) tickRate ,
xFrames = xFrames ,
xSeconds = xSeconds ,
useBT = useBT ,
pauseBT = pauseBT ,
curBehaviourTreeID = ( currentBehaviour ) ? currentBehaviour . ID : string . Empty ,
purposeBehaviourTreeID = ( purposeBehaviourTree ) ? purposeBehaviourTree . ID : string . Empty ,
combatBehaviourTreeID = ( combatBehaviourTree ) ? combatBehaviourTree . ID : string . Empty ,
keepTicking = keepTicking ,
startingCellID = startingCellID ,
saveCellID = ( myCellInfo ! = null ) ? myCellInfo . cell . ID : cellIDOfLastSaved ,
runtimeSaveCellID = runtimeStartingCell ,
hipsPosition = bodyData . hips . transform . position ,
hipsRotation = bodyData . hips . transform . rotation ,
isUsingPurposeBehaviour = isUsingPurposeBehaviour ,
cellIDMovingTo = IsReachingCellID ,
enteredInADoorLastTime = enteredInADoorLastTime ,
doorEnteredID = doorEnteredID ,
overrideShouldFollowMainTarget = overrideShouldFollowMainTarget ,
overrideShouldFollowMainTargetActive = overrideShouldFollowMainTargetActive ,
enteredInADoorLastTimeOverridePos = enteredInADoorLastTimeOverridePos ,
enteredDoorLastTimePos = enteredDoorLastTimePos ,
enteredDoorLastTimeRot = enteredDoorLastTimeRot ,
radius = radius ,
sphereYoffset = sphereYOffset ,
sphereZoffset = sphereForwardOffset ,
lookAtVector3IfStopped = lookAtVector3IfStopped ,
vector3ToLookAtIfStopped = vector3ToLookAtIfStopped ,
followTargetOutsideOfCell = followTargetOutsideOfCell
} ;
// Save other data
if ( usesRagdoll )
newData . ragdollSaveData = ragdoll . ToSaveData ( ) ;
// Save factions
for ( int i = 0 ; i < belongsToFactions . Count ; i + + )
newData . allFactions . Add ( belongsToFactions [ i ] . ID ) ;
newData . entityHesFighting = new List < string > ( ) ;
for ( int i = 0 ; i < enemyTargets . Count ; i + + )
newData . entityHesFighting . Add ( enemyTargets [ i ] . m_entity . entityID ) ;
// See if we can save Target
if ( mainTarget ! = null )
{
ITargetable targetable = mainTarget . GetComponent < ITargetable > ( ) ;
if ( targetable ! = null )
{
newData . mainTargetID = targetable . GetID ( ) ;
newData . mainTargetType = ( int ) targetable . GetTargetableType ( ) ;
}
else
newData . mainTargetID = string . Empty ;
}
else
newData . mainTargetID = string . Empty ;
// Save PurposeState
newData . purposeState = purposeState ;
return newData ;
}
public void DestroyThis ( )
{
SaveOnFile ( ) ;
myCellInfo . aiInWorld . Remove ( entityID ) ;
Destroy ( this . gameObject ) ;
}
// Travel doors
public bool teleportIfUnloadedWhileGoingToDoor = true ;
public bool teleportIfWalkableSurfaceEnds = true ;
public bool isReachingDoor ;
public Door doorIsReaching ;
public string IsReachingCellID ;
public bool enteredInADoorLastTime ;
public bool enteredInADoorLastTimeOverridePos ;
public Vector3 enteredDoorLastTimePos ;
public Quaternion enteredDoorLastTimeRot ;
public string doorEnteredID ;
public string aiwuhje ;
[ContextMenu("GO TO CELL")]
public void ae ( )
{
// Assign Purpose
MoveToCell ( aiwuhje ) ;
SetNewBehaviourTree ( false , "BTP_FollowMainTargetOnly" ) ;
SwitchBehaviourTree ( false ) ;
purposeState . AssignPurpose ( this , mainTarget . gameObject , PurposeClearTypes . ClearOnTeleportToCell , new PurposeStateClearsOnData ( "FarmHouseT" ) , "BTP_RoamRelax001" ) ;
}
public override void Update ( )
{
base . Update ( ) ;
if ( followTargetOutsideOfCell )
FollowTargetOutsideOfCell ( ) ;
}
public static Transform GetTransformToDoor ( string _cellID )
{
bool doorFound = false ;
// Lookup the dictionary that contains "Cell -> DoorsToThatCell"
var pref = PersistentReferences . PersistentReferenceManager . instance ;
if ( pref . cellsDoorsDictionary . ContainsKey ( _cellID ) )
{
string doorID = pref . cellsDoorsDictionary [ _cellID ] ;
if ( pref . cellsDoorsDictionary . ContainsKey ( _cellID ) & & pref . refs . ContainsKey ( doorID ) )
{
return pref . refs [ doorID ] . transform ;
}
}
else
{
// The AI has to move to a door (or a PRef) that will bring him there
Door fDoor = null ;
// Foreach active cell
foreach ( KeyValuePair < string , CellInformation > cellinfo in CellInformation . activeCells )
{
// For each Door in the current cell
foreach ( KeyValuePair < string , Door > door in cellinfo . Value . allDoors )
{
if ( door . Value . teleports & & door . Value . toCell ! = null & & door . Value . toCell . ID = = _cellID )
{
// We found the door in the active cells
doorFound = true ;
fDoor = door . Value ;
return fDoor . transform ;
}
}
if ( doorFound )
break ;
}
}
if ( ! doorFound ) // Door is not in active cells, so look for pref otherwise teleport
{
Debug . Log ( "Tried to GetTransformToDoor: " + _cellID + " | But couldn't be found because it's unloaded and there is no Persistent Reference to that door." ) ;
}
return null ;
}
public void MoveToCell ( string _cellID , Vector3 onceEnteredPos , Quaternion onceEnteredRot )
{
enteredInADoorLastTimeOverridePos = true ;
enteredDoorLastTimePos = onceEnteredPos ;
enteredDoorLastTimeRot = onceEnteredRot ;
MoveToCell ( _cellID ) ;
}
/// <summary>
/// Says to the AI to find a door and move to the desired cell after
/// </summary>
public void MoveToCell ( string _cellID )
{
bool doorFound = false ;
// Lookup the dictionary that contains "Cell -> DoorsToThatCell"
var pref = PersistentReferences . PersistentReferenceManager . instance ;
if ( pref . cellsDoorsDictionary . ContainsKey ( _cellID ) )
{
string doorID = pref . cellsDoorsDictionary [ _cellID ] ;
if ( pref . cellsDoorsDictionary . ContainsKey ( _cellID ) & & pref . refs . ContainsKey ( doorID ) )
{
doorIsReaching = null ; // Door is reaching is null because we're following a persistent reference transform
// it will be assigned later, when the AI reaches the door
PersistentReferences . PersistentReference TToDoor = pref . refs [ doorID ] ;
IsReachingCellID = _cellID ;
doorFound = true ;
toDoorTransform = TToDoor . transform ;
isReachingDoor = true ;
SetTarget ( TToDoor . gameObject ) ;
StartCoroutine ( MovingToCell ( _cellID , doorID ) ) ;
}
}
else
{
// The AI has to move to a door (or a PRef) that will bring him there
Door fDoor = null ;
// Foreach active cell
foreach ( KeyValuePair < string , CellInformation > cellinfo in CellInformation . activeCells )
{
// For each Door in the current cell
foreach ( KeyValuePair < string , Door > door in cellinfo . Value . allDoors )
{
if ( door . Value . teleports & & door . Value . toCell ! = null & & door . Value . toCell . ID = = _cellID )
{
// We found the door in the active cells
doorFound = true ;
fDoor = door . Value ;
IsReachingCellID = fDoor . toCell . ID ;
doorEnteredID = fDoor . linkedDoorObjRef ;
break ;
}
}
if ( doorFound )
break ;
}
// Door was in active cells
if ( doorFound & & fDoor ! = null )
{
isReachingDoor = true ;
doorIsReaching = fDoor ;
toDoorTransform = doorIsReaching . groundPivot ;
SetTarget ( doorIsReaching . groundPivot . gameObject ) ;
StartCoroutine ( MovingToCell ( _cellID ) ) ;
}
}
if ( ! doorFound ) // Door is not in active cells, so look for pref otherwise teleport
{
Debug . Log ( "The AI can't get to Cell: " + _cellID + " | Because it's unloaded and there is no Persistent Reference to that door." ) ;
}
}
public Transform toDoorTransform ;
IEnumerator MovingToCell ( string _cellID , string doorID = null )
{
// Wait until the AI reaches the door, the Teleport to the other cell
isReachingDoor = true ;
while ( Vector3 . Distance ( transform . position , toDoorTransform . position ) > stoppingDistance | | m_isInCombat )
{
yield return null ;
}
m_Anim . CrossFade ( "OpenDoor" , 0.1f ) ;
yield return new WaitForSeconds ( m_Anim . GetCurrentAnimatorClipInfo ( 0 ) . Length ) ;
if ( doorIsReaching = = null & & doorID ! = null )
{
if ( myCellInfo . allDoors . ContainsKey ( doorID ) )
{
// Assign the door and the linkedDoorObjRef
doorIsReaching = myCellInfo . allDoors [ doorID ] ;
doorEnteredID = doorIsReaching . linkedDoorObjRef ;
}
}
isReachingDoor = false ;
IsReachingCellID = string . Empty ;
enteredInADoorLastTime = true ;
SaveOnFile ( ) ;
// We reached the door
TeleportToCell ( _cellID ) ;
yield return null ;
}
public void TeleportToCell ( string _cellID )
{
//// PURPOSE_CALLBACK: ClearOnTeleportToCell \\\\\
if ( purposeState ! = null & & purposeState . IsAssigned & & purposeState . clearsOn = = PurposeClearTypes . ClearOnTeleportToCell )
{
if ( purposeState . clearsOnData . stringData = = _cellID )
{
// We did it
purposeState . CompletePurpose ( ) ;
}
}
var aiData = SaveSystemManager . instance . saveFile . AIData ;
// Check if cellDictionary contains the cell we're putting this npc into
if ( aiData . aiCellDictionary . ContainsKey ( _cellID ) )
{
if ( ! aiData . aiCellDictionary [ _cellID ] . allIDs . Contains ( entityID ) )
aiData . aiCellDictionary [ _cellID ] . allIDs . Add ( entityID ) ;
}
else
{
aiData . aiCellDictionary . Add ( _cellID , new AIIDList ( ) ) ;
aiData . aiCellDictionary [ _cellID ] . allIDs . Add ( entityID ) ;
}
// Manually set the Cell and coor and manually save
string oldSaveCell = myCellInfo . cell . ID ;
myCellInfo . aiInWorld . Remove ( entityID ) ;
AISaveData thisAIData = ToSaveData ( ) ;
thisAIData . isReachingDoor = false ;
thisAIData . cellIDMovingTo = string . Empty ;
thisAIData . saveCellID = _cellID ;
thisAIData . position = new Vector3 ( 0 , 0 , 0 ) ;
// If the AI has never been saved we can safely move it to the new cell
if ( ! aiData . aiDictionary . ContainsKey ( entityID ) )
aiData . aiDictionary . Add ( entityID , thisAIData ) ;
else
{
aiData . aiDictionary [ entityID ] = thisAIData ;
// Remove it from his old aiCellDicitonary
if ( aiData . aiCellDictionary . ContainsKey ( oldSaveCell ) )
aiData . aiCellDictionary [ oldSaveCell ] . allIDs . Remove ( entityID ) ;
}
myCellInfo . aiInWorld . Remove ( entityID ) ;
// If the cell is loaded (or cached, move this gameobject, otherwise destroy it, it will be instantiated next time the cell will be loaded)
if ( CellInformation . activeCells . ContainsKey ( _cellID ) )
CellInformation . activeCells [ _cellID ] . AddToAIToInstantiateIfCached ( entityID ) ;
Destroy ( this . gameObject ) ;
}
public bool followTargetOutsideOfCell = false ;
/// <summary>
/// This function detects if the AI was following its target and that target changed cell, if its enabled, this AI will be teleported where the
/// </summary>
public void FollowTargetOutsideOfCell ( )
{
// If its the player
if ( isAlive & & shouldFollowMainTarget & & mainTarget . CompareTag ( "Player" ) & &
myCellInfo ! = null & & WorldManager . instance ! = null )
{
// Perform the check
if ( myCellInfo . cell ! = null & & myCellInfo . cell . ID ! = WorldManager . instance . currentCenterCell . ID & & this . isInOfflineMode )
{
myCellInfo . aiInWorld . Remove ( this . entityID ) ;
// Teleport out
transform . SetParent ( CellInformation . activeCells [ WorldManager . instance . currentCenterCell . ID ] . aiContainer ) ;
CellInformation . activeCells [ WorldManager . instance . currentCenterCell . ID ] . aiInWorld . Add ( entityID , this ) ;
myCellInfo = CellInformation . activeCells [ WorldManager . instance . currentCenterCell . ID ] ;
transform . position = ( mainTarget . position + mainTarget . forward ) + ( transform . right * UnityEngine . Random . Range ( - 1 , 1 ) ) ;
onlineComponents . OnGoingOnline ( ) ;
SaveOnFile ( ) ;
}
}
}
private void OnChangeHairColor ( Color co ) {
SkinnedMeshRenderer Hair = bodyData . hair ;
Material [ ] MyMats = Hair . materials ;
for ( int i = 0 ; i < MyMats . Length ; i + + )
MyMats [ i ] . SetColor ( "_Diffuse" , co ) ;
}
private void OnChangeLipsColor ( Color co ) {
SkinnedMeshRenderer Lips = bodyData . Lips ;
Material [ ] MyMats = Lips . materials ;
for ( int i = 0 ; i < MyMats . Length ; i + + )
MyMats [ i ] . SetColor ( "_Diffuse" , co ) ;
}
private void OnChangeSkinColor ( Color co ) {
SkinnedMeshRenderer Torso = bodyData . upperbody ;
SkinnedMeshRenderer Legs = bodyData . lowerbody ;
SkinnedMeshRenderer Head = bodyData . head ;
SkinnedMeshRenderer Arms = bodyData . arms ;
SkinnedMeshRenderer Hands = bodyData . hands ;
SkinnedMeshRenderer Feet = bodyData . feet ;
SkinnedMeshRenderer Lips = bodyData . Lips ;
Material [ ] MyTorsoMats = Torso . materials ;
Material [ ] MyLegsMats = Legs . materials ;
Material [ ] MyHeadMats = Head . materials ;
Material [ ] MyArmsMats = Arms . materials ;
Material [ ] MyHandsMats = Hands . materials ;
Material [ ] MyFeetMats = Feet . materials ;
Material [ ] MyLipsMats = Lips . materials ;
for ( int i = 0 ; i < MyTorsoMats . Length ; i + + )
MyTorsoMats [ i ] . SetColor ( "_Diffuse" , co ) ;
for ( int i = 0 ; i < MyLegsMats . Length ; i + + )
MyLegsMats [ i ] . SetColor ( "_Diffuse" , co ) ;
for ( int i = 0 ; i < MyHeadMats . Length ; i + + )
MyHeadMats [ i ] . SetColor ( "_Diffuse" , co ) ;
for ( int i = 0 ; i < MyArmsMats . Length ; i + + )
MyArmsMats [ i ] . SetColor ( "_Diffuse" , co ) ;
for ( int i = 0 ; i < MyHandsMats . Length ; i + + )
MyHandsMats [ i ] . SetColor ( "_Diffuse" , co ) ;
for ( int i = 0 ; i < MyFeetMats . Length ; i + + )
MyFeetMats [ i ] . SetColor ( "_Diffuse" , co ) ;
}
}
2023-03-28 13:16:30 -04:00
}