using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class damageHitReactionSystem : MonoBehaviour { [Header ("Main Settings")] [Space] public bool hitReactionsEnabled = true; public string defaultCategoryReaction = "Melee"; [Space] [Header ("Reaction Probability Settings")] [Space] [Range (0, 1)] public float probabilityToActivateReactionMultiplier = 1; public bool canChangeProbabilityMultiplierExternally = true; [Space] public bool ignoreMinWaitTimeToPlayHitReactionAgain; public bool ignoreMinAmountToReceiveDamage; [Space] [Header ("Hit Reaction Settings")] [Space] public List hitReactionInfoList = new List (); [Space] public List damageStateInfoList = new List (); [Space] [Header ("Debug")] [Space] public bool showDebugPrint; public bool hitReactionActive; public int currentBlockID; public string currentReactionCategory; public bool damageReactionPaused; public bool ignoreParryActive; [Space] [Header ("Events Settings")] [Space] public bool useMainEventOnHitReactionFound; public UnityEvent mainEventOnHitReactionFound; [Space] [Header ("Components")] [Space] public Transform playerControllerTransform; public playerActionSystem mainPlayerActionSystem; hitReactionInfo currentHitReactionInfo; Coroutine perfectBlockCoroutine; float lastTimeBlockActive; int currentBlockIDToCheck; hitDamageDirection currenthitDamageDirection; public void checkDamageReceived (float damageReceived, Vector3 damagePosition, GameObject objectBlocked) { if (!hitReactionsEnabled || !hitReactionActive) { return; } if (damageReactionPaused) { return; } checkHitReaction (damageReceived, 0, damagePosition, false, false, false, objectBlocked, -1); } public void checkDamageReceivedTemporally (float damageReceived, Vector3 damagePosition, GameObject objectBlocked) { if (!hitReactionsEnabled) { return; } if (damageReactionPaused) { return; } checkHitReaction (damageReceived, 0, damagePosition, false, false, false, objectBlocked, -1); } public void checkDamageBlocked (float damageReceived, float originalDamageAmountReceived, Vector3 damagePosition, bool damageBlocked, GameObject objectBlocked) { if (!hitReactionsEnabled || !hitReactionActive) { return; } if (damageReactionPaused) { return; } checkHitReaction (damageReceived, originalDamageAmountReceived, damagePosition, damageBlocked, true, false, objectBlocked, -1); } public void checkDamageReceivedUnblockable (float damageReceived, Vector3 damagePosition, GameObject objectBlocked) { if (!hitReactionsEnabled || !hitReactionActive) { return; } if (damageReactionPaused) { return; } checkHitReaction (damageReceived, 0, damagePosition, false, false, true, objectBlocked, -1); } public void checkReactionToTriggerExternally (float damageReceived, Vector3 damagePosition, GameObject objectBlocked) { if (!hitReactionsEnabled || !hitReactionActive) { return; } if (damageReactionPaused) { return; } checkHitReaction (damageReceived, 0, damagePosition, false, false, false, objectBlocked, -1); } public void activateDamageReactionByID (float damageReceived, Vector3 damagePosition, GameObject objectBlocked, int damageReactionID) { if (!hitReactionsEnabled) { return; } if (damageReactionPaused) { return; } checkHitReaction (damageReceived, 0, damagePosition, false, false, false, objectBlocked, damageReactionID); } void checkHitReaction (float damageReceived, float originalDamageAmountReceived, Vector3 damagePosition, bool damageBlocked, bool blockDamageActive, bool unblockableDamage, GameObject objectBlocked, int damageReactionID) { if (damageReactionID == -1) { Vector3 damageDirection = damagePosition - playerControllerTransform.position; damageDirection = damageDirection / damageDirection.magnitude; float damageAngle = Vector3.SignedAngle (playerControllerTransform.forward, damageDirection, playerControllerTransform.up); float damageAngleAbs = Mathf.Abs (damageAngle); currenthitDamageDirection = hitDamageDirection.front; bool isPlayerMovingOn3dWorld = mainPlayerActionSystem.isPlayerMovingOn3dWorld (); if (isPlayerMovingOn3dWorld) { if (damageAngleAbs > 45 && damageAngleAbs < 135) { if (damageAngle < 0) { currenthitDamageDirection = hitDamageDirection.left; } else { currenthitDamageDirection = hitDamageDirection.right; } } else if (damageAngleAbs > 135) { currenthitDamageDirection = hitDamageDirection.backward; } } else { if (!isFacingToAttacker (objectBlocked.transform.position)) { currenthitDamageDirection = hitDamageDirection.backward; } } if (showDebugPrint) { print (currenthitDamageDirection); } } int hitReactionIndex = -1; float maxDamageFound = -1; bool damageStateFound = false; if (damageReceived > 0) { for (int i = 0; i < damageStateInfoList.Count; i++) { damageStateInfo currentDamageStateInfo = damageStateInfoList [i]; if (!damageStateFound && currentDamageStateInfo.damageStateEnabled) { if (currentDamageStateInfo.damageStateRange.x <= damageReceived && currentDamageStateInfo.damageStateRange.y >= damageReceived) { if (showDebugPrint) { print (currentDamageStateInfo.Name); } currentDamageStateInfo.eventOnDamageState.Invoke (); damageStateFound = true; } } } } if (!damageStateFound) { for (int i = 0; i < damageStateInfoList.Count; i++) { damageStateInfo currentDamageStateInfo = damageStateInfoList [i]; if (!damageStateFound && currentDamageStateInfo.damageStateEnabled) { if (currentDamageStateInfo.useAsDefaultState) { currentDamageStateInfo.eventOnDamageState.Invoke (); if (showDebugPrint) { print (currentDamageStateInfo.Name); } damageStateFound = true; } } } } bool canCheckReaction = false; if (currentReactionCategory == "") { currentReactionCategory = defaultCategoryReaction; } bool searchingReactionByID = damageReactionID > -1; bool canCheckRegularReaction = false; for (int i = 0; i < hitReactionInfoList.Count; i++) { currentHitReactionInfo = hitReactionInfoList [i]; if (currentHitReactionInfo.reactionEnabled) { if (searchingReactionByID) { if (hitReactionIndex == -1) { if (damageReactionID == currentHitReactionInfo.damageReactionID) { canCheckRegularReaction = true; if (showDebugPrint) { print ("Damage reaction activated by ID, checking reaction" + currentHitReactionInfo.Name); } } } } else { canCheckRegularReaction = false; if (currentHitReactionInfo.reactionCategory == currentReactionCategory) { if (currentHitReactionInfo.reactionOnBlock == blockDamageActive) { if (!blockDamageActive || currentHitReactionInfo.blockID == currentBlockID) { canCheckRegularReaction = true; } } } } if (canCheckRegularReaction) { canCheckReaction = false; if (damageReceived >= currentHitReactionInfo.minAmountToReceiveDamage) { canCheckReaction = true; } if (ignoreMinAmountToReceiveDamage) { canCheckReaction = true; } if (blockDamageActive && (damageBlocked || damageReceived == 0)) { canCheckReaction = true; } if (showDebugPrint) { print (currentHitReactionInfo.Name + " " + damageReceived + " " + blockDamageActive + " " + damageBlocked + " " + canCheckReaction); } if (canCheckReaction) { if (currentHitReactionInfo.minAmountToReceiveDamage > maxDamageFound) { if (damageReactionID > -1 || (currentHitReactionInfo.damageDirection == hitDamageDirection.anyDirection || currentHitReactionInfo.damageDirection == currenthitDamageDirection)) { maxDamageFound = currentHitReactionInfo.minAmountToReceiveDamage; hitReactionIndex = i; } } } } } } if (hitReactionIndex > -1) { activateHitReactionByIndex (hitReactionIndex, damageReceived, originalDamageAmountReceived, unblockableDamage, objectBlocked); } } bool isFacingToAttacker (Vector3 attackerPosition) { Vector3 directionDamage = (attackerPosition - playerControllerTransform.position).normalized; Vector3 playerForward = playerControllerTransform.forward; bool isFacingDamage = Vector2.Dot (directionDamage, playerForward) > 0; if (isFacingDamage) { return true; } return false; } float lastTimeHitReaction; float currentReactionDelayUntilNextReaction = 0; void activateHitReactionByIndex (int hitReactionIndex, float damageReceived, float originalDamageAmountReceived, bool unblockableDamage, GameObject objectBlocked) { if (hitReactionIndex > -1) { currentHitReactionInfo = hitReactionInfoList [hitReactionIndex]; if (currentHitReactionInfo.useProbabilityToActivateReaction) { float probabilityToActivateReaction = Random.Range (0, 100); float currentProbability = currentHitReactionInfo.probabilityToActivateReaction * probabilityToActivateReactionMultiplier; if (currentProbability < probabilityToActivateReaction) { return; } } if (!currentHitReactionInfo.ignoreReactionDelayFromPreviousReaction) { if (currentReactionDelayUntilNextReaction > 0 && Time.time < currentReactionDelayUntilNextReaction + lastTimeHitReaction) { if (showDebugPrint) { print ("\n reaction delay from previous reaction is active, disabling reaction activation \n"); } return; } currentReactionDelayUntilNextReaction = 0; if (currentHitReactionInfo.setReactionDelayUntilNextReaction) { currentReactionDelayUntilNextReaction = currentHitReactionInfo.reactionDelayUntilNextReaction; } } lastTimeHitReaction = Time.time; bool checkMinWaitTimeResult = false; if (Time.time > currentHitReactionInfo.minWaitTimeToPlayHitReactionAgain + currentHitReactionInfo.lastTimeHitReaction) { checkMinWaitTimeResult = true; } if (ignoreMinWaitTimeToPlayHitReactionAgain) { checkMinWaitTimeResult = true; } if (checkMinWaitTimeResult) { currentHitReactionInfo.lastTimeHitReaction = Time.time; bool canActivateHitReaction = true; bool perfectBlockActivated = false; if (!currentHitReactionInfo.activateHitReactionOnPerfectBlock && !unblockableDamage) { if (currentHitReactionInfo.reactionOnBlock && currentHitReactionInfo.canUsePerfectBlock) { if (lastTimeBlockActive != 0) { if (Time.time < lastTimeBlockActive + currentHitReactionInfo.maxTimeToPerfectBlock) { canActivateHitReaction = false; perfectBlockActivated = true; } } } } if (showDebugPrint) { print ("\n\n"); print (currentHitReactionInfo.Name + " " + damageReceived + " " + playerControllerTransform.name + " " + perfectBlockActivated + " " + canActivateHitReaction); } if (canActivateHitReaction) { if (useMainEventOnHitReactionFound) { mainEventOnHitReactionFound.Invoke (); } if (currentHitReactionInfo.stopPreviousActionIfActive) { mainPlayerActionSystem.stopAllActions (); } if (currentHitReactionInfo.playHitAction) { mainPlayerActionSystem.activateCustomAction (currentHitReactionInfo.hitActionName); } } else { if (perfectBlockActivated) { if (currentHitReactionInfo.stopPreviousActionIfActive) { mainPlayerActionSystem.stopAllActions (); } if (currentHitReactionInfo.playHitAction) { mainPlayerActionSystem.activateCustomAction (currentHitReactionInfo.hitActionNameOnPerfectBlock); } } } if (currentHitReactionInfo.useStaminaOnBlock) { float currentDamageReceived = damageReceived; if (currentDamageReceived == 0) { currentDamageReceived = originalDamageAmountReceived; } float staminaAmountToUse = currentHitReactionInfo.staminaToUseMultiplier * currentDamageReceived; currentHitReactionInfo.eventToUseStamina.Invoke (staminaAmountToUse); } if (canActivateHitReaction) { currentHitReactionInfo.eventOnHit.Invoke (); } else { if (perfectBlockActivated) { currentHitReactionInfo.eventOnHitReactionOnPerfectBlock.Invoke (); if (currentHitReactionInfo.useRemoteEvent) { bool useRemoteEvents = false; if (objectBlocked != null) { if (currentHitReactionInfo.checkObjectsToUseRemoteEventsOnDamage) { if ((1 << objectBlocked.layer & currentHitReactionInfo.layerToUseRemoteEventsOnDamage.value) == 1 << objectBlocked.layer) { useRemoteEvents = true; } } else { useRemoteEvents = true; } if (useRemoteEvents) { remoteEventSystem currentRemoteEventSystem = objectBlocked.GetComponent (); if (currentRemoteEventSystem != null) { int remoteEventNameListCount = currentHitReactionInfo.remoteEventNameList.Count; for (int j = 0; j < remoteEventNameListCount; j++) { currentRemoteEventSystem.callRemoteEvent (currentHitReactionInfo.remoteEventNameList [j]); } if (!ignoreParryActive) { if (currentHitReactionInfo.useRemoteEventToEnableParryInteraction) { for (int j = 0; j < currentHitReactionInfo.remoteEventToEnableParryInteractionNameList.Count; j++) { currentRemoteEventSystem.callRemoteEvent (currentHitReactionInfo.remoteEventToEnableParryInteractionNameList [j]); } } } } } } } } } } } } public void checkDamageStateTemporally (float damageReceived, bool damageBlockedByShieldSystem) { // print (damageReceived); bool damageStateFound = false; if (damageReceived > 0) { if (damageBlockedByShieldSystem) { damageReceived /= 2; } for (int i = 0; i < damageStateInfoList.Count; i++) { damageStateInfo currentDamageStateInfo = damageStateInfoList [i]; if (!damageStateFound && currentDamageStateInfo.damageStateEnabled) { if (currentDamageStateInfo.damageStateRange.x <= damageReceived && currentDamageStateInfo.damageStateRange.y >= damageReceived) { // print (currentDamageStateInfoName); currentDamageStateInfo.eventOnDamageState.Invoke (); damageStateFound = true; } } } } if (!damageStateFound) { for (int i = 0; i < damageStateInfoList.Count; i++) { damageStateInfo currentDamageStateInfo = damageStateInfoList [i]; if (!damageStateFound && currentDamageStateInfo.damageStateEnabled) { if (currentDamageStateInfo.useAsDefaultState) { currentDamageStateInfo.eventOnDamageState.Invoke (); // print (currentDamageStateInfo.Name); damageStateFound = true; } } } } } public void setHitReactionActiveState (bool state) { hitReactionActive = state; } public bool getHitReactionActiveState () { return hitReactionActive; } public void setHitReactionBlockIDValue (int newBlockID) { currentBlockID = newBlockID; if (currentBlockID != -1) { currentBlockIDToCheck = -1; for (int i = 0; i < hitReactionInfoList.Count; i++) { hitReactionInfo temporalReationInfo = hitReactionInfoList [i]; if (currentBlockIDToCheck == -1 && temporalReationInfo.reactionOnBlock && temporalReationInfo.reactionCategory.Equals (currentReactionCategory) && temporalReationInfo.canUsePerfectBlock && temporalReationInfo.blockID == currentBlockID) { float probabilityForPerfectBlock = Random.Range (0, 100); if (temporalReationInfo.probabilityForPerfectBlock >= probabilityForPerfectBlock) { currentBlockIDToCheck = i; } } } if (currentBlockIDToCheck > -1) { lastTimeBlockActive = Time.time; setPerfectBlockDuration (); } else { lastTimeBlockActive = 0; } } else { lastTimeBlockActive = 0; stopSetPerfectBlockDurationCoroutine (); if (currentBlockIDToCheck > -1) { hitReactionInfoList [currentBlockIDToCheck].eventOnDisablePerfectBlock.Invoke (); currentBlockIDToCheck = -1; } } } public void setPerfectBlockDuration () { stopSetPerfectBlockDurationCoroutine (); perfectBlockCoroutine = StartCoroutine (setPerfectBlockDurationCoroutine ()); } void stopSetPerfectBlockDurationCoroutine () { if (perfectBlockCoroutine != null) { StopCoroutine (perfectBlockCoroutine); } } IEnumerator setPerfectBlockDurationCoroutine () { hitReactionInfo temporalReationInfo = hitReactionInfoList [currentBlockIDToCheck]; temporalReationInfo.eventOnEnablePerfectBlock.Invoke (); float maxTimeToPerfectBlock = temporalReationInfo.maxTimeToPerfectBlock; var currentWaitTime = new WaitForSeconds (maxTimeToPerfectBlock); yield return currentWaitTime; temporalReationInfo.eventOnDisablePerfectBlock.Invoke (); } public void setCurrentReactionCategory (string newCategory) { currentReactionCategory = newCategory; } public void setNewProbabilityToActivateReactionMultiplierValue (float newValue) { if (!canChangeProbabilityMultiplierExternally) { return; } probabilityToActivateReactionMultiplier = newValue; } public void setHitReactionsEnabledState (bool state) { hitReactionsEnabled = state; } public void setDamageReactionPausedState (bool state) { damageReactionPaused = state; } public bool isDamageReactionPaused () { return damageReactionPaused; } public void setIgnoreParryActiveState (bool state) { ignoreParryActive = state; } public void setHitReactionsEnabledStateFromEditor (bool state) { setHitReactionsEnabledState (state); updateComponent (); } void updateComponent () { GKC_Utils.updateComponent (this); GKC_Utils.updateDirtyScene ("Updating Damage Reaction System " + gameObject.name, gameObject); } [System.Serializable] public class hitReactionInfo { [Header ("Main Settings")] [Space] public string Name; public bool reactionEnabled = true; public int damageReactionID = -1; public string reactionCategory; public float minAmountToReceiveDamage; public hitDamageDirection damageDirection; public bool useProbabilityToActivateReaction = true; public float probabilityToActivateReaction = 100; public float minWaitTimeToPlayHitReactionAgain; [Space] [Header ("Pause Reaction Activation Settings")] [Space] public bool setReactionDelayUntilNextReaction; public float reactionDelayUntilNextReaction; public bool ignoreReactionDelayFromPreviousReaction; [Space] [Header ("Action Settings")] [Space] public bool playHitAction; public string hitActionName; public string hitActionNameOnPerfectBlock; public bool stopPreviousActionIfActive; [Space] [Header ("Hit Reaction On Block Settings")] [Space] public bool reactionOnBlock; public int blockID; public bool useStaminaOnBlock; public float staminaToUseMultiplier = 1; [Space] public eventParameters.eventToCallWithAmount eventToUseStamina; [Space] [Header ("Perfect Block Settings")] [Space] public bool canUsePerfectBlock; public bool activateHitReactionOnPerfectBlock; public float probabilityForPerfectBlock = 100; public float maxTimeToPerfectBlock = 0.5f; [Space] public UnityEvent eventOnEnablePerfectBlock; public UnityEvent eventOnDisablePerfectBlock; [Space] [Header ("Events Settings")] [Space] public UnityEvent eventOnHit; [HideInInspector] public float lastTimeHitReaction; public UnityEvent eventOnHitReactionOnPerfectBlock; [Space] [Header ("Remote Events Settings")] [Space] public bool checkObjectsToUseRemoteEventsOnDamage; public LayerMask layerToUseRemoteEventsOnDamage; public bool useRemoteEvent; public List remoteEventNameList; [Space] [Space] public bool useRemoteEventToEnableParryInteraction; public List remoteEventToEnableParryInteractionNameList; } public enum hitDamageDirection { front, backward, right, left, anyDirection } [System.Serializable] public class damageStateInfo { public string Name; public bool damageStateEnabled; public Vector2 damageStateRange; public bool useAsDefaultState; public UnityEvent eventOnDamageState; } }