/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
#if !ULTIMATE_CHARACTER_CONTROLLER_EXTENSION_DEBUG
namespace Opsive.UltimateCharacterController.Editor.References
{
#if !FIRST_PERSON_CONTROLLER || !THIRD_PERSON_CONTROLLER
using Opsive.Shared.Utility;
using Opsive.UltimateCharacterController.Camera.ViewTypes;
using Opsive.UltimateCharacterController.Character.MovementTypes;
using Opsive.UltimateCharacterController.Character.Abilities;
using Opsive.UltimateCharacterController.Character.Abilities.Items;
using Opsive.UltimateCharacterController.Editor.Inspectors.Utility;
using Opsive.UltimateCharacterController.Editor.Managers;
using Opsive.UltimateCharacterController.StateSystem;
using System.Collections.Generic;
#endif
using Opsive.UltimateCharacterController.Demo.References;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
[InitializeOnLoad]
public class ObjectRemover
{
private static Scene s_ActiveScene;
///
/// Registers for the scene change callback.
///
static ObjectRemover()
{
EditorApplication.update += Update;
}
///
/// The scene has been changed.
///
private static void Update()
{
var scene = SceneManager.GetActiveScene();
if (scene == s_ActiveScene || Application.isPlaying) {
return;
}
s_ActiveScene = scene;
// Only the add-ons and integrations demo scene should be affected.
var scenePath = s_ActiveScene.path.Replace("\\", "/");
if (!scenePath.Contains("UltimateCharacterController/Add-Ons") && !scenePath.Contains("UltimateCharacterController/Integrations")) {
return;
}
// Find the object which contains the objects that should be removed.
var objectReferences = GameObject.FindObjectOfType();
ProcessObjectReferences(objectReferences, true);
}
///
/// Removes the objects specified by the object references object.
///
private static void ProcessObjectReferences(ObjectReferences objectReferences, bool fromScene)
{
if (objectReferences == null) {
return;
}
RemoveObjects(objectReferences.RemoveObjects);
objectReferences.RemoveObjects = null;
#if !FIRST_PERSON_CONTROLLER
RemoveObjects(objectReferences.FirstPersonObjects);
objectReferences.FirstPersonObjects = null;
#endif
#if !THIRD_PERSON_CONTROLLER
RemoveObjects(objectReferences.ThirdPersonObjects);
objectReferences.ThirdPersonObjects = null;
#endif
#if !ULTIMATE_CHARACTER_CONTROLLER_SHOOTER
RemoveObjects(objectReferences.ShooterObjects);
objectReferences.ShooterObjects = null;
#endif
#if !ULTIMATE_CHARACTER_CONTROLLER_MELEE
RemoveObjects(objectReferences.MeleeObjects);
objectReferences.MeleeObjects = null;
#endif
#if !FIRST_PERSON_CONTROLLER || !THIRD_PERSON_CONTROLLER
// Remove any view types and states that are no longer valid.
Opsive.UltimateCharacterController.Camera.CameraController cameraController;
if (fromScene) {
cameraController = GameObject.FindObjectOfType();
} else {
cameraController = objectReferences.GetComponent();
}
if (cameraController != null) {
cameraController.DeserializeViewTypes();
var viewTypes = new List(cameraController.ViewTypes);
for (int i = viewTypes.Count - 1; i > -1; --i) {
if (viewTypes[i] == null) {
viewTypes.RemoveAt(i);
continue;
}
viewTypes[i].States = RemoveUnusedStates(viewTypes[i].States);
}
cameraController.ViewTypeData = Serialization.Serialize(viewTypes);
cameraController.ViewTypes = viewTypes.ToArray();
InspectorUtility.SetDirty(cameraController);
}
// Remove any movement types and states that are no longer valid.
Character.UltimateCharacterLocomotion characterLocomotion;
if (fromScene) {
characterLocomotion = GameObject.FindObjectOfType();
} else {
characterLocomotion = objectReferences.GetComponent();
}
if (characterLocomotion != null) {
characterLocomotion.DeserializeMovementTypes();
var movementTypes = new List(characterLocomotion.MovementTypes);
for (int i = movementTypes.Count - 1; i > -1; --i) {
if (movementTypes[i] == null) {
movementTypes.RemoveAt(i);
continue;
}
movementTypes[i].States = RemoveUnusedStates(movementTypes[i].States);
}
characterLocomotion.MovementTypeData = Serialization.Serialize(movementTypes);
characterLocomotion.MovementTypes = movementTypes.ToArray();
#if FIRST_PERSON_CONTROLLER
characterLocomotion.SetMovementType(UltimateCharacterController.Utility.UnityEngineUtility.GetType(characterLocomotion.FirstPersonMovementTypeFullName));
#else
characterLocomotion.SetMovementType(UltimateCharacterController.Utility.UnityEngineUtility.GetType(characterLocomotion.ThirdPersonMovementTypeFullName));
#endif
// Ensure the animator is pointing to the correct reference.
var animator = characterLocomotion.GetComponent();
if (animator != null && animator.runtimeAnimatorController == null) {
animator.runtimeAnimatorController = ManagerUtility.FindAnimatorController(null);
InspectorUtility.SetDirty(animator);
}
// Check for unused ability states.
var abilities = new List(characterLocomotion.GetSerializedAbilities());
for (int i = abilities.Count - 1; i > -1; --i) {
if (abilities[i] == null) {
abilities.RemoveAt(i);
continue;
}
abilities[i].States = RemoveUnusedStates(abilities[i].States);
}
characterLocomotion.AbilityData = Serialization.Serialize(abilities);
characterLocomotion.Abilities = abilities.ToArray();
// Check for unused item ability states.
var itemAbilities = new List(characterLocomotion.GetSerializedItemAbilities());
for (int i = itemAbilities.Count - 1; i > -1; --i) {
if (itemAbilities[i] == null) {
itemAbilities.RemoveAt(i);
continue;
}
itemAbilities[i].States = RemoveUnusedStates(itemAbilities[i].States);
}
characterLocomotion.ItemAbilityData = Serialization.Serialize(itemAbilities);
characterLocomotion.ItemAbilities = itemAbilities.ToArray();
InspectorUtility.SetDirty(characterLocomotion);
// Update the inventory.
var inventory = characterLocomotion.GetComponent();
if (inventory != null) {
var loadout = new List(inventory.DefaultLoadout);
for (int i = loadout.Count - 1; i > -1; --i) {
if (loadout[i].ItemDefinition == null) {
loadout.RemoveAt(i);
}
}
inventory.DefaultLoadout = loadout.ToArray();
InspectorUtility.SetDirty(inventory);
}
var itemSetManager = characterLocomotion.GetComponent();
if (itemSetManager != null) {
var categoryItemSets = itemSetManager.CategoryItemSets;
for (int i = 0; i < categoryItemSets.Length; ++i) {
for (int j = categoryItemSets[i].ItemSetList.Count - 1; j > -1; --j) {
var nullItemIdentifier = true;
for (int k = 0; k < categoryItemSets[i].ItemSetList[j].Slots.Length; ++k) {
if (categoryItemSets[i].ItemSetList[j].Slots[k] != null) {
nullItemIdentifier = false;
break;
}
}
if (nullItemIdentifier) {
categoryItemSets[i].ItemSetList.RemoveAt(j);
}
}
};
InspectorUtility.SetDirty(itemSetManager);
}
}
#if !THIRD_PERSON_CONTROLLER
// Set the shadow caster for the first person only objects.
var shadowCaster = ManagerUtility.FindInvisibleShadowCaster(null);
if (shadowCaster != null) {
for (int i = 0; i < objectReferences.ShadowCasterObjects.Length; ++i) {
if (objectReferences.ShadowCasterObjects[i] == null) {
continue;
}
var renderers = objectReferences.ShadowCasterObjects[i].GetComponentsInChildren();
for (int j = 0; j < renderers.Length; ++j) {
var materials = renderers[j].sharedMaterials;
for (int k = 0; k < materials.Length; ++k) {
materials[k] = shadowCaster;
}
renderers[j].sharedMaterials = materials;
InspectorUtility.SetDirty(renderers[j]);
}
}
}
#endif
var items = objectReferences.GetComponentsInChildren();
for (int i = 0; i < items.Length; ++i) {
CheckItem(items[i].gameObject);
}
// Ensure all of the states point to a preset
StateBehavior[] stateBehaviors;
if (fromScene) {
stateBehaviors = GameObject.FindObjectsOfType();
} else {
stateBehaviors = objectReferences.GetComponentsInChildren(true);
}
if (stateBehaviors != null) {
for (int i = 0; i < stateBehaviors.Length; ++i) {
stateBehaviors[i].States = RemoveUnusedStates(stateBehaviors[i].States);
InspectorUtility.SetDirty(stateBehaviors[i]);
}
}
#endif
// Some doors should be locked.
#if !FIRST_PERSON_CONTROLLER
LockDoors(objectReferences.FirstPersonDoors);
#endif
#if !THIRD_PERSON_CONTROLLER
LockDoors(objectReferences.ThirdPersonDoors);
#endif
for (int i = 0; i < objectReferences.NestedReferences.Length; ++i) {
var nestedObject = objectReferences.NestedReferences[i];
if (nestedObject == null) {
continue;
}
GameObject nestedRoot = null;
if (PrefabUtility.IsPartOfPrefabAsset(nestedObject)) {
nestedRoot = PrefabUtility.LoadPrefabContents(AssetDatabase.GetAssetPath(objectReferences.NestedReferences[i]));
nestedObject = nestedRoot.GetComponent();
}
ProcessObjectReferences(nestedObject, false);
if (nestedRoot != null) {
PrefabUtility.SaveAsPrefabAsset(nestedRoot, AssetDatabase.GetAssetPath(objectReferences.NestedReferences[i]));
PrefabUtility.UnloadPrefabContents(nestedRoot);
}
}
UnpackPrefab(objectReferences);
Object.DestroyImmediate(objectReferences, true);
}
///
/// Removes the specified objects.
///
private static void RemoveObjects(Object[] objects)
{
if (objects == null) {
return;
}
for (int i = objects.Length - 1; i > -1; --i) {
if (objects[i] == null || PrefabUtility.GetPrefabAssetType(objects[i]) == PrefabAssetType.MissingAsset) {
continue;
}
if (objects[i] is GameObject && (objects[i] as GameObject).transform.parent == null && AssetDatabase.GetAssetPath(objects[i]).Length > 0 &&
PrefabUtility.GetPrefabAssetType(objects[i]) == PrefabAssetType.Regular) {
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(objects[i]));
} else {
UnpackPrefab(objects[i]);
Object.DestroyImmediate(objects[i], true);
}
}
}
#if !FIRST_PERSON_CONTROLLER || !THIRD_PERSON_CONTROLLER
///
/// Ensure the item only has the valid states.
///
private static void CheckItem(GameObject gameObject)
{
if (gameObject == null || gameObject.GetComponent() == null) {
return;
}
var magicItems = gameObject.GetComponents();
for (int i = 0; i < magicItems.Length; ++i) {
magicItems[i].DeserializeBeginActions(false);
var beginActions = magicItems[i].BeginActions;
if (beginActions != null) {
for (int j = 0; j < beginActions.Length; ++j) {
beginActions[j].States = RemoveUnusedStates(beginActions[j].States);
}
magicItems[i].BeginActionData = Serialization.Serialize(beginActions);
}
magicItems[i].DeserializeCastActions(false);
var castActions = magicItems[i].CastActions;
if (castActions != null) {
for (int j = 0; j < castActions.Length; ++j) {
castActions[j].States = RemoveUnusedStates(castActions[j].States);
}
magicItems[i].CastActionData = Serialization.Serialize(castActions);
}
magicItems[i].DeserializeImpactActions(false);
var impactActions = magicItems[i].ImpactActions;
if (impactActions != null) {
for (int j = 0; j < impactActions.Length; ++j) {
impactActions[j].States = RemoveUnusedStates(impactActions[j].States);
}
magicItems[i].ImpactActionData = Serialization.Serialize(impactActions);
}
magicItems[i].DeserializeEndActions(false);
var endActions = magicItems[i].EndActions;
if (endActions != null) {
for (int j = 0; j < endActions.Length; ++j) {
endActions[j].States = RemoveUnusedStates(endActions[j].States);
}
magicItems[i].EndActionData = Serialization.Serialize(endActions);
}
InspectorUtility.SetDirty(magicItems[i]);
}
}
#endif
///
/// Unpacks the prefab root.
///
/// The object that should be unpacked.
private static void UnpackPrefab(Object obj)
{
if (obj != null && PrefabUtility.IsPartOfAnyPrefab(obj)) {
var root = PrefabUtility.GetNearestPrefabInstanceRoot(obj);
if (root != null) {
PrefabUtility.UnpackPrefabInstance(root, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
}
}
#if !FIRST_PERSON_CONTROLLER || !THIRD_PERSON_CONTROLLER
///
/// Removes any states whose preset will be exlcluded.
///
private static State[] RemoveUnusedStates(State[] stateArray)
{
var states = new List(stateArray);
var stateRemovals = new HashSet();
for (int i = states.Count - 2; i > -1; --i) {
var preset = states[i].Preset;
if (preset == null) {
stateRemovals.Add(states[i].Name);
states.RemoveAt(i);
}
}
for (int i = 0; i < states.Count; ++i) {
if (states[i].BlockList == null) {
continue;
}
var blockList = new List(states[i].BlockList);
for (int j = blockList.Count - 1; j > -1; --j) {
if (stateRemovals.Contains(blockList[j])) {
blockList.RemoveAt(j);
}
}
states[i].BlockList = blockList.ToArray();
}
return states.ToArray();
}
///
/// Locks the unused doors.
///
private static void LockDoors(GameObject[] doors)
{
if (doors == null) {
return;
}
for (int i = 0; i < doors.Length; ++i) {
if (doors[i] == null) {
continue;
}
var door = doors[i].GetComponent();
if (door == null) {
continue;
}
door.PermanentlyLocked = true;
InspectorUtility.SetDirty(door);
}
}
#endif
}
}
#endif