You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

410 lines
15 KiB
C#

using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MalbersAnimations.Weapons
{
[AddComponentMenu("Malbers/Weapons/MBow")]
public class MBow : MShootable
{
#region Bow Stuff
public Transform knot; //Point of the bow to put the arrow (End)
public bool KnotToHand { get; set; }
/// <summary>Max Bending the Bow can do</summary>
public float MaxTension;
[Range(0, 1)] public float BowTensionPrev;
public Transform[] UpperBn; //Upper Chain of the bow
public Transform[] LowerBn; //Upper Chain of the bow
/// <summary> Default Rotation of the Upper Bow Bones </summary>
[SerializeField] private Quaternion[] UpperBnInitRot;
/// <summary> Default Rotation of the Lower Bow Bones </summary>
[SerializeField] private Quaternion[] LowerBnInitRot;
/// <summary> Maximun Rotation of the Upper Bow Bones </summary>
[SerializeField] private Quaternion[] UpperBnMaxRot;
/// <summary> Maximun Rotation of the Lower Bow Bones </summary>
[SerializeField] private Quaternion[] LowerBnMaxRot;
[Tooltip("Default position of the Knot to return to when the String is on its default position")]
public Vector3 DefaultPosKnot;
public Vector3 KnotHandOffset;
public Vector3 RotUpperDir = -Vector3.forward;
public Vector3 RotLowerDir = Vector3.forward;
/// <summary> Is the Bow Bones Setted Correctly?... Is this bow functional?? </summary>
public bool BowIsSet = false;
public override bool IsAiming
{
get => base.IsAiming;
set
{
base.IsAiming = value;
if (!value)
{
DestroyProjectileInstance(); //IF you finish aiming then destroy the Instance of the projectile
}
}
}
#endregion
public virtual void SerializeBow()
{
if (UpperBn == null || LowerBn == null)
{
Debug.LogWarning("Please fill the Upper and Low Joints on the Bow");
BowIsSet = false;
return;
}
if (UpperBn.Length == 0 || LowerBn.Length == 0)
{
Debug.LogWarning("Please fill the Upper and Low Joints on the Bow");
BowIsSet = false;
return;
}
ChargeCurrentTime = 0;
UpperBnInitRot = new Quaternion[UpperBn.Length]; //Get the Initial Upper ChainRotation
LowerBnInitRot = new Quaternion[LowerBn.Length]; //Get the Initial Lower ChainRotation
UpperBnMaxRot = new Quaternion[UpperBn.Length]; //Get the Initial Upper ChainRotation
LowerBnMaxRot = new Quaternion[LowerBn.Length]; //Get the Initial Lower ChainRotation
for (int i = 0; i < UpperBn.Length; i++)
{
if (UpperBn[i] == null)
{
BowIsSet = false;
return;
}
UpperBnInitRot[i] = UpperBn[i].localRotation;
UpperBnMaxRot[i] = Quaternion.Euler(RotUpperDir * MaxTension) * UpperBnInitRot[i];
}
for (int i = 0; i < LowerBn.Length; i++)
{
if (LowerBn[i] == null)
{
BowIsSet = false;
return;
}
LowerBnInitRot[i] = LowerBn[i].localRotation;
LowerBnMaxRot[i] = Quaternion.Euler(RotLowerDir * MaxTension) * LowerBnInitRot[i];
}
BowIsSet = true;
Debug.Log("The Initial Position and Rotation of the bow has been stored corretly");
}
/// <summary> Called by the Animator</summary>
public virtual void BowKnotToHand(bool enabled)
{
KnotToHand = enabled;
if (!KnotToHand) RestoreKnot();
}
/// <summary>Updates the BowKnot position in the center of the hand if is active</summary>
protected void BowKnotInHand(IMWeaponOwner RC)
{
if (KnotToHand)
{
knot.position = IsRightHanded ?
RC.LeftHand.TransformPoint(KnotHandOffset) :
RC.RightHand.TransformPoint(KnotHandOffset);
knot.rotation = Quaternion.LookRotation((AimOrigin.position - knot.position).normalized, -Gravity);
}
}
/// <summary>Rotate and modify the bow Bones to bend it from Min = 0 to Max = 1</summary>
public virtual void BendBow(float normalizedTime)
{
if (!BowIsSet) return;
for (int i = 0; i < UpperBn.Length; i++)
{
UpperBn[i].localRotation = Quaternion.Lerp(UpperBnInitRot[i], UpperBnMaxRot[i], normalizedTime); //Bend the Upper Chain on the Bow
}
for (int i = 0; i < LowerBn.Length; i++)
{
LowerBn[i].localRotation = Quaternion.Lerp(LowerBnInitRot[i], LowerBnMaxRot[i], normalizedTime); //Bend the Lower Chain of the Bow
}
if (knot && AimOrigin) Debug.DrawRay(knot.position, knot.forward, Color.red);
}
/// <summary> Is called when the Rider is not holding the string of the bow </summary>
public virtual void RestoreKnot()
{
knot.localPosition = DefaultPosKnot;
DestroyProjectileInstance();
}
/// <summary> Charge the Weapon!! </summary>
internal override void Attack_Charge(IMWeaponOwner RC, float time)
{
base.Attack_Charge(RC, time);
if (IsCharging) BendBow(ChargedNormalized); //Bend the Bow
}
public override void ResetCharge()
{
base.ResetCharge();
BendBow(0);
if (Sounds.Length > 5 && WeaponSound.isPlaying && WeaponSound.clip == Sounds[5]) WeaponSound.Stop();
}
internal override void LateUpdateWeaponIK(IMWeaponOwner RC)
{
IKProfile?.LateUpdate_IK(RC);
}
internal override void LateWeaponModification(IMWeaponOwner RC)
{
BowKnotInHand(RC);
}
/// CallBack from the RiderCombat Layer in the Animator to reproduce a sound on the weapon
public override void PlaySound(int ID)
{
if (ID < Sounds.Length && Sounds[ID] != null)
{
var newSound = Sounds[ID];
if (WeaponSound && !playingSound && gameObject.activeInHierarchy)
{
if (ID == 5 && CanCharge) //THIS IS THE SOUND FOR BEND THE BOW
{
WeaponSound.pitch = 1.03f / ChargeTime;
StartCoroutine(BowChargeTimePlay(newSound));
}
else
{
WeaponSound.pitch = 1;
//HACK FOR THE SOUND
this.Delay_Action(2, () =>
{
WeaponSound.PlayOneShot(newSound);
playingSound = false;
}
);
}
}
}
}
IEnumerator BowChargeTimePlay(AudioClip sound)
{
while (ChargedNormalized == 0) yield return null;
WeaponSound.PlayOneShot(sound);
}
public override void ResetWeapon()
{
base.ResetWeapon();
RestoreKnot();
}
#if UNITY_EDITOR
protected override void Reset()
{
base.Reset();
AimLimit = new AnimationCurve(new Keyframe[] { new Keyframe(0, 1), new Keyframe(1.25f, 1), new Keyframe(1.5f, 0), new Keyframe(2f, 0) });
}
#endif
//Editor variables
[HideInInspector] public bool BonesFoldout, proceduralfoldout;
[HideInInspector] public int LowerIndex, UpperIndex;
}
#region Inspector
#if UNITY_EDITOR
[CanEditMultipleObjects, CustomEditor(typeof(MBow))]
public class MBowEditor : MShootableEditor
{
string[] axis = { "+X", "-X", "+Y", "-Y", "+Z", "-Z" };
SerializedProperty
UpperBn, LowerBn, UpperIndex, BowTension, LowerIndex, MaxTension, DefaultPosKnot, knot, MaxArmTension, KnotHandOffset,
BonesFoldout, RotUpperDir, RotLowerDir, BowIsSet;
MBow mBow;
private void OnEnable()
{
mBow = (MBow)target;
mShoot = (MBow)target;
SetOnEnable();
Tabs2 = new string[] { "Bow" , "Shootable", "Sounds", "Events" };
MaxArmTension = serializedObject.FindProperty("MaxArmTension");
// AimWeight = serializedObject.FindProperty("AimWeight");
UpperBn = serializedObject.FindProperty("UpperBn");
BowIsSet = serializedObject.FindProperty("BowIsSet");
knot = serializedObject.FindProperty("knot");
KnotHandOffset = serializedObject.FindProperty("KnotHandOffset");
DefaultPosKnot = serializedObject.FindProperty("DefaultPosKnot");
LowerBn = serializedObject.FindProperty("LowerBn");
UpperIndex = serializedObject.FindProperty("UpperIndex");
LowerIndex = serializedObject.FindProperty("LowerIndex");
BowTension = serializedObject.FindProperty("BowTensionPrev");
MaxTension = serializedObject.FindProperty("MaxTension");
BonesFoldout = serializedObject.FindProperty("BonesFoldout");
RotUpperDir = serializedObject.FindProperty("RotUpperDir");
RotLowerDir = serializedObject.FindProperty("RotLowerDir");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
MalbersEditor.DrawDescription("Bow Weapons Properties");
WeaponInspector(false);
serializedObject.ApplyModifiedProperties();
}
protected override void WeaponInspector(bool showAim = true)
{
Editor_Tabs1.intValue = GUILayout.Toolbar(Editor_Tabs1.intValue, Tabs1);
if (Editor_Tabs1.intValue != Tabs1.Length) Editor_Tabs2.intValue = Tabs2.Length;
Editor_Tabs2.intValue = GUILayout.Toolbar(Editor_Tabs2.intValue, Tabs2);
if (Editor_Tabs2.intValue != Tabs2.Length) Editor_Tabs1.intValue = Tabs1.Length;
//First Tabs
int Selection = Editor_Tabs1.intValue;
if (Selection == 0) DrawWeapon(showAim);
else if (Selection == 1) DrawDamage();
else if (Selection == 2) DrawIKWeapon();
else if (Selection == 3) DrawExtras();
//2nd Tabs
Selection = Editor_Tabs2.intValue;
if (Selection == 0) DrawBow();
else if (Selection == 1) DrawAdvancedWeapon();
else if (Selection == 2) DrawSound();
else if (Selection == 3) DrawEvents();
}
protected void DrawBow()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
BonesFoldout.boolValue = EditorGUILayout.Foldout(BonesFoldout.boolValue, new GUIContent("Bow Joints", "All References for the Bow Bones"));
if (BonesFoldout.boolValue)
{
EditorGUILayout.PropertyField(knot , new GUIContent("Knot", "Transform reference for the Bow middle String point"));
EditorGUILayout.PropertyField(KnotHandOffset, new GUIContent("Knot Hand Offset","Offset to Position the Knot to the Hand"));
if (knot.objectReferenceValue != null)
{
EditorGUILayout.BeginHorizontal();
if (!Application.isPlaying) EditorGUILayout.PropertyField(DefaultPosKnot, new GUIContent("Def Knot Pos"));
if (GUILayout.Button(new GUIContent("C", "Calculate the default position of the Knot Point"), EditorStyles.miniButton, GUILayout.Width(18)))
{
DefaultPosKnot.vector3Value = (knot.objectReferenceValue as Transform).localPosition;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.PropertyField(UpperBn, new GUIContent("Upper Chain", "Upper bone chain of the bow"), true);
EditorGUILayout.PropertyField(LowerBn, new GUIContent("Lower Chain", "Lower bone chain of the bow"), true);
EditorGUILayout.EndVertical();
}
EditorGUI.indentLevel--;
if (GUILayout.Button("Store Initial Bow Position|Rotation"))
{
mBow.SerializeBow();
EditorUtility.SetDirty(mBow);
serializedObject.ApplyModifiedProperties();
}
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
UpperIndex.intValue = EditorGUILayout.Popup("Upper Rot Axis", UpperIndex.intValue, axis);
LowerIndex.intValue = EditorGUILayout.Popup("Lower Rot Axis", LowerIndex.intValue, axis);
EditorGUILayout.EndVertical();
RotUpperDir.vector3Value = Axis(UpperIndex.intValue);
RotLowerDir.vector3Value = Axis(LowerIndex.intValue);
EditorGUI.BeginChangeCheck();
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.PropertyField(MaxTension, new GUIContent("Max Tension", "Max Angle that the Bow can Bent"));
if (BowIsSet.boolValue)
{
EditorGUILayout.PropertyField(BowTension, new GUIContent("Bow Tension (P)", "Previews the Bow tension"));
if (BowTension.floatValue > 0)
EditorGUILayout.HelpBox("This is for visual purpose only, please return the Bow Tension to 0", MessageType.Warning);
}
EditorGUILayout.EndVertical();
}
if (EditorGUI.EndChangeCheck())
{
if (MaxTension.floatValue < 0) MaxTension.floatValue = 0;
if (BowIsSet.boolValue)
mBow.BendBow(BowTension.floatValue);
EditorUtility.SetDirty(mBow);
}
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "MBow Inspector");
}
}
protected override string CustomEventsHelp()
{
return "\n\nOn Load Arrow: Invoked when the arrow is instantiated.\n (GameObject) the instance of the Arrow. \n\nOnHold: Invoked when the bow is being bent (0 to 1)\n\nOn Release Arrow: Invoked when the Arrow is released.\n (GameObject) the instance of the Arrow.";
}
Vector3 Axis(int Index)
{
switch (Index)
{
case 0: return Vector3.right;
case 1: return -Vector3.right;
case 2: return Vector3.up;
case 3: return -Vector3.up;
case 4: return Vector3.forward;
case 5: return -Vector3.forward;
default: return Vector3.zero;
}
}
}
#endif
#endregion
}