Firstborn/Assets/RPG Creation Kit/Scripts/AI/RckAI.cs
Schaken-Mods d2c5e0c92c Update 4/25/23
Worked on the Editor's Actor creator. NPC's can now have custom colored skins and hairs.
2023-04-26 00:28:58 -05:00

1079 lines
33 KiB
C#

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
{
[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>();
}
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);
}
}
}