using UnityEngine; using System.Collections.Generic; using System; using NobleMuffins.LimbHacker.Guts; using UnityEngine.Events; namespace NobleMuffins.LimbHacker { public class Hackable : MonoBehaviour, ISliceable { [Header ("Main Settings")] [Space] public InfillMode infillMode = InfillMode.Sloppy; public bool ignoreUpdateLastObjectSpeed; public bool ignoreDestroyOriginalObject; public bool setCustomIDOnSliceSpieces; public string randomString = ""; public bool destructionPending = false; [Space] [Header ("Parts To Slice Settings")] [Space] public Transform [] severables = new Transform [0]; [Space] [Header ("Events Settings")] [Space] public UnityEvent eventsOnIgnoreDestroyOriginalObject; [Space] [Header ("Components")] [Space] public surfaceToSlice mainSurfaceToSlice; public GameObject objectToSlice; public UnityEngine.Object alternatePrefab = null; public Material infillMaterial = null; public Dictionary maximumTiltBySeverableName = new Dictionary (); bool valuesInitialized; bool updateLastObjectSpeed; Vector3 lastSpeed = Vector3.zero; public string getRandomString () { return randomString; } public void setRandomString (string newValue) { if (randomString.Equals ("")) { randomString = newValue; } } public void initializeValues () { if (objectToSlice == null) { objectToSlice = gameObject; } foreach (Transform bodyPart in severables) { if (bodyPart != null) { ChildOfHackable referencer = bodyPart.GetComponent (); if (referencer == null) referencer = bodyPart.gameObject.AddComponent (); referencer.parentHackable = this; } } valuesInitialized = true; } void checkInitializeValues () { if (!valuesInitialized) { initializeValues (); } } public event EventHandler Sliced; public void Slice (Vector3 positionInWorldSpace, Vector3 normalInWorldSpace) { if (destructionPending) { return; } var decisionMaker = objectToSlice.GetComponent (); string jointName = null; float rootTipProgression = 0f; if (LimbHackerAgent.DetermineSlice (this, positionInWorldSpace, ref jointName, ref rootTipProgression) && (decisionMaker == null || decisionMaker.ShouldHack (jointName))) { LimbHackerAgent.instance.SeverByJoint (objectToSlice, jointName, rootTipProgression, normalInWorldSpace); } } public void handleSlice (GameObject [] results, Vector4 planeInWorldSpace, Vector3 focalPointInWorldSpace) { bool originalRemainsAfterSlice = false; for (int i = 0; i < results.Length; i++) originalRemainsAfterSlice |= results [i] == objectToSlice; destructionPending = !originalRemainsAfterSlice; AbstractSliceHandler [] handlers = objectToSlice.GetComponents (); foreach (AbstractSliceHandler handler in handlers) { handler.handleSlice (results); } if (Sliced != null) { Sliced (this, new SliceEventArgs (new Plane (planeInWorldSpace, planeInWorldSpace.w), focalPointInWorldSpace, results)); if (!ignoreUpdateLastObjectSpeed) { if (updateLastObjectSpeed && lastSpeed != Vector3.zero) { for (int i = 0; i < results.Length; i++) { if (results [i] != null) { Component [] currentRigidbody = results [i].GetComponentsInChildren (typeof (Rigidbody)); foreach (Rigidbody child in currentRigidbody) { if (child.gameObject.activeInHierarchy) { child.linearVelocity = lastSpeed; } } // print (results [i].name); } } } updateLastObjectSpeed = false; lastSpeed = Vector3.zero; } } } public bool cloneAlternate (Dictionary hierarchyPresence) { if (alternatePrefab == null) { return false; } else { AbstractSliceHandler [] handlers = objectToSlice.GetComponents (); bool result = false; if (handlers.Length == 0) { result = true; } else { foreach (AbstractSliceHandler handler in handlers) { result |= handler.cloneAlternate (hierarchyPresence); } } return result; } } private readonly Queue pendingSlices = new Queue (); bool sliceWaitingToFinish; Vector3 normalInWorldSpace; private List suppressUntilContactCeases = new List (); class PendingSlice { public PendingSlice (Vector3 _point, ISliceable _target) { point = _point; target = _target; } public readonly Vector3 point; public readonly ISliceable target; } void LateUpdate () { if (sliceWaitingToFinish) { while (pendingSlices.Count > 0) { PendingSlice pendingSlice = pendingSlices.Dequeue (); var component = pendingSlice.target as MonoBehaviour; if (component != null) { var targetGameObject = component.gameObject; if (suppressUntilContactCeases.Contains (targetGameObject) == false) { pendingSlice.target.Sliced += PendingSlice_target_Sliced; pendingSlice.target.Slice (pendingSlice.point, normalInWorldSpace); } } } sliceWaitingToFinish = false; ContactCeased (objectToSlice); } } void PendingSlice_target_Sliced (object sender, SliceEventArgs e) { if (e.Parts.Length > 1) { suppressUntilContactCeases.AddRange (e.Parts); } } private void ContactCeased (GameObject other) { if (suppressUntilContactCeases.Contains (other)) { suppressUntilContactCeases.Remove (other); } } public void activateSlice (GameObject objectToSlice, Vector3 point, Vector3 newNormalInWorldSpaceValue, bool updateLastObjectSpeedValue, Vector3 lastSpeedValue) { checkInitializeValues (); ISliceable sliceable = objectToSlice.GetComponent (typeof (ISliceable)) as ISliceable; if (sliceable != null) { pendingSlices.Enqueue (new PendingSlice (point, sliceable)); normalInWorldSpace = newNormalInWorldSpaceValue; sliceWaitingToFinish = true; updateLastObjectSpeed = updateLastObjectSpeedValue; lastSpeed = lastSpeedValue; } } public void setDestructionPending (bool state) { destructionPending = state; } } }