Super Knight : Enter the Dungeon
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

842 lines
23 KiB

using Spine.Unity;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
public enum IFF { Our, Enemy }
public enum DamageType { None, Fixed, Rate, RateHP, Bomb }
public class Character : MonoBehaviour
{
public enum State { HP_Change, AH_Change, Mana_Change, Inventory, EquipItem, Status, ActiveItem }
public EventConnect ec = new EventConnect();
[HideInInspector]
public MapManager map;
public string NAME = "";
CharacterData data;
[Space(10)]
public IFF iff = IFF.Our;
public bool ALL_HIT = false;
public Collider hitBox;
[HideInInspector]
public int layerEnemy = 0;
private void Awake()
{
map = (MapManager)FindObjectOfType(typeof(MapManager));
skill = GetComponent<Skill>();
motion = GetComponent<Motion>();
statusEffect = GetComponent<StatusEffect>();
}
private void Start()
{
tag = $"{iff}";
//gameObject.layer = LayerMask.NameToLayer($"{(iff)}");
layerEnemy = ALL_HIT ? LayerMask.NameToLayer($"HitAll") : LayerMask.NameToLayer($"Hit{(iff == IFF.Our ? IFF.Enemy : IFF.Our)}");
hitBox.gameObject.layer = LayerMask.NameToLayer($"{(iff)}");
gameObject.tag = $"{(iff)}";
if (matHP30Down != null)
{
matHP30Down_Copy = new Material(matHP30Down);
matHP30Down_Copy.SetTexture("_MainTex", mainTexture);
scvHP30Down.Init(matHP30Down_Copy);
}
matHit_Copy = new Material(matHit);
matHit_Copy.SetTexture("_MainTex", mainTexture);
if (matHitResist != null)
{
matHitResist_Copy = new Material(matHitResist);
matHitResist_Copy.SetTexture("_MainTex", mainTexture);
}
matHide_Copy = new Material(matHide);
matHide_Copy.SetTexture("_MainTex", mainTexture);
scvHide.Init(matHide_Copy);
}
Collider col;
void OnCollider(bool ON)
{
if (col == null)
col = GetComponent<Collider>();
col.enabled = ON;
}
/// <summary>
/// 캐릭터의 초기화를 진행합니다
/// </summary>
/// <param name="HP"></param>
/// <param name="HP_Max"></param>
public void OnInitialized(int HP, int HP_Max)
{
if(HP == 0)
{
if (data == null)
data = DataManager.Instance.dataMonster.DataGet(NAME);
HP = HP_Max = data.HP;
ATK = data.ATK;
DLAATK = data.DLA_ATK;
SPDMOVE = data.SPD_MOVE;
motion.RunMotionTimeScaleChanged(data.MOVE_TIME_SCALE);
}
statusEffect.OnInitialized();//생성했을때 상태이상 초기화
this.HP_Max = HP_Max;
ec.Invoke((int)State.HP_Change, this.HP = HP);
OnCollider(true);
Material_Restore();
OnDefense = null;
hitBox.gameObject.SetActive(true);
Show_Start();
}
//public void ProjectileCreate(Projectile projectile, Vector3 dir)
//{
// if (projectile != null)
// {
// projectile.spdMovePer = SPDATK_PER;
// projectile.OnInitialized(this, layerEnemy, dir, RANGE);
// }
//}
[Space(10)]
public int ATK = 1;
/// <summary>
/// 캐릭터의 공격력이 변경 되어 이를 적용합니다.
/// </summary>
/// <param name="value"></param>
/// <param name="ADD"></param>
public void ATKChanged(int value, bool ADD = true)
{
if (ADD)
ATK += value;
else
ATK = value;
ec.Invoke((int)Character.State.Status, null);
}
public float ATK_PER = 1f;
/// <summary>
/// 캐릭터의 공경력 % 가 변경 되어 이를 적용합니다.
/// </summary>
/// <param name="value"></param>
/// <param name="ADD"></param>
public void ATKRateChanged(float value, bool ADD = true)
{
if (ADD)
ATK_PER += value;
else
ATK_PER = value;
ec.Invoke((int)Character.State.Status, null);
}
/// <summary>
/// 현재 데미지를 계산합니다.
/// </summary>
/// <returns></returns>
public int CalculateATK()
{
return (int)(ATK * ATK_PER);
}
[Space(10)]
public float SPDATK_PER = 1f;
[System.Serializable]
public class SpeedAttackEvent : UnityEvent<float> { }
[FormerlySerializedAs("onSpeedAttacked")]
[SerializeField]
[Space(10)]
private SpeedAttackEvent onSpeedAttacked = new SpeedAttackEvent();
/// <summary>
/// 공격 모션의 속도가 변경 되어 이를 적용합니다.
/// </summary>
/// <param name="value"></param>
/// <param name="ADD"></param>
public void SpeedAttackChanged(float value, bool ADD = true)
{
if (ADD)
SPDATK_PER += value;
else
SPDATK_PER = value;
onSpeedAttacked.Invoke(SPDATK_PER);
ec.Invoke((int)Character.State.Status, null);
}
[Space(10)]
public float DLAATK = 0;
public float DLAATK_PER = 1f;
public float DLAATK_MIN = 0.2f;
//[System.Serializable]
//public class DelayAttackEvent : UnityEvent<float, bool> { }
//[FormerlySerializedAs("onDelayAttacked")]
//[SerializeField]
//[Space(10)]
//private DelayAttackEvent onDelayAttacked = new DelayAttackEvent();
/// <summary>
/// 공격 후 딜레이가 변경되어 이를 적용합니다.
/// </summary>
/// <param name="add"></param>
/// <param name="RATE"></param>
public void DelayAttackChanged(float add, bool RATE = true)
{
if (RATE)
{
DLAATK_PER += add;
//onDelayAttacked.Invoke(DLAATK_PER, RATE);
}
else
{
DLAATK = add;
//onDelayAttacked.Invoke(DLAATK, RATE);
}
//ec.Invoke((int)Character.State.Status, null);
}
/// <summary>
/// 현재 후 딜레이를 계산합니다.
/// </summary>
/// <returns></returns>
public float CalculateAttackDelay()
{
float apply = DLAATK * DLAATK_PER;
apply = apply < DLAATK_MIN ? DLAATK_MIN : apply;
return apply;
}
[Space(10)]
public float RANGE = 0;
//[System.Serializable]
//public class RangeEvent : UnityEvent<float, bool> { }
//[FormerlySerializedAs("onRangeed")]
//[SerializeField]
//[Space(10)]
//private RangeEvent onRangeed = new RangeEvent();
/// <summary>
/// 사거리가 변경 되어 이를 적용합니다.
/// </summary>
/// <param name="add"></param>
/// <param name="RATE"></param>
public void RangeChanged(float add, bool RATE)
{
if (RATE)
{
RANGE += add;
//onRangeed.Invoke(DLAATK_PER, RATE);
}
else
{
RANGE = add;
//onRangeed.Invoke(DLAATK, RATE);
}
//ec.Invoke((int)Character.State.Status, null);
}
[Space(10)]
public float SPDMOVE = 40;//기본 이동 속도, 캐릭터 마다 다름
public float SPDMOVE_PER = 1f;
[System.Serializable]
public class SpeedMoveEvent : UnityEvent<float> { }
[FormerlySerializedAs("onSpeedMoveed")]
[SerializeField]
[Space(10)]
private SpeedMoveEvent onSpeedMoveed = new SpeedMoveEvent();
/// <summary>
/// 이동 속도가 변경되어 이를 적용합니다.
/// </summary>
/// <param name="add"></param>
public void SpeedMoveChanged(float add)
{
SPDMOVE_PER += add;
onSpeedMoveed.Invoke(SPDMOVE_PER);
ec.Invoke((int)Character.State.Status, null);
}
/// <summary>
/// 현재 이동속도를 계산합니다.
/// </summary>
/// <returns></returns>
public float CalculateMoveSpeed()
{
float spdMoveApply = SPDMOVE * SPDMOVE_PER;
return spdMoveApply < 60 ? spdMoveApply : 60;
}
int LIFE_MAX = 20;
public bool LifeMaxCheck()
{
return HP_Max + AH <= LIFE_MAX;
}
[Space(10)]
public int HP = 3;
public bool DieCheck()
{
return HP < 1;
}
public int HP_Max = 4;
/// <summary>
/// HP 에 데미지를 적용합니다.
/// </summary>
/// <param name="Damage"></param>
public void CalculateHP(int Damage)
{
if (0 < AH)
{
AH -= Damage;
Damage = AH < 0 ? -AH : 0;
}
HP -= Damage;
}
/// <summary>
/// HP 가 회복 가능한 상태인지 확인합니다.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool RestoreCheck(int value)
{
return HP < HP_Max;
}
/// <summary>
/// HP 를 회복합니다.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Restore(int value)
{
if (HP < HP_Max)
{
HP += value;
HP = HP_Max < HP ? HP_Max : HP;
ec.Invoke((int)State.HP_Change, HP);
return true;
}
return false;
}
/// <summary>
/// HP 를 최대 HP 의 % 로 회복합니다.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Restore_MaxRate(float value)
{
HP += (int)(HP_Max * value);
HP = HP_Max < HP ? HP_Max : HP;
Material_Restore();
ec.Invoke((int)State.HP_Change, HP);
return true;
}
private void OnDisable()
{
//scvHide.isPlay = false;
scvHide.Stop();
}
public int AH = 0;
/// <summary>
/// 아머하트가 변경되어 이를 적용합니다.
/// </summary>
/// <param name="value"></param>
public void OnChanged_ArmorHeart(int value)
{
AH = value;
ec.Invoke((int)State.HP_Change, 0);
}
/// <summary>
/// 아머하트가 회복 가능한지 확인합니다.
/// </summary>
/// <param name="add"></param>
/// <returns></returns>
public bool ArmorHeartCheck(int add)
{
int MAX = LIFE_MAX - HP_Max;
return AH + add <= MAX;
}
/// <summary>
/// 아머하트를 회복합니다.
/// </summary>
/// <param name="add"></param>
/// <returns></returns>
public bool OnAdded_ArmorHeart(int add)
{
int MAX = LIFE_MAX - HP_Max;
if (AH + add <= MAX)
{
AH = AH + add;
ec.Invoke((int)State.HP_Change, HP);
return true;
}
return false;
}
public int MANA = 0;
public int MANA_MAX = 0;
/// <summary>
/// 마나 회복 가능한지 확인합니다.
/// </summary>
/// <returns></returns>
public bool ManaCheck()
{
return MANA < MANA_MAX;
}
/// <summary>
/// 마나를 회복 혹은 사용 합니다.
/// </summary>
/// <param name="add"></param>
/// <returns></returns>
public bool OnAdded_Mana(int add)
{
if (MANA + add <= MANA_MAX)
{
MANA += add;
MANA = MANA_MAX < MANA ? MANA_MAX : MANA;
ec.Invoke((int)State.Mana_Change, MANA);
return true;
}
return false;
}
public System.Action<Character, Vector3> OnDie;//Character -> 때린 캐릭터
public System.Action<Character, Vector3, float, bool, System.Action> OnHit;//Character -> 죽은 캐릭터
public System.Action<Character> OnDefense;
public System.Action<Character> OnResurrection;
/// <summary>
/// 공격을 받았으며 이를 처리합니다.
/// HitBox.cs -> OnDamaged 에서 호출
/// </summary>
/// <param name="data"></param>
public void Damaged(object data)
{
if (NO_DAMAGE)
return;
HitCheck hitCheck = (HitCheck)data;
//if (hitCheck.owner == this && hitCheck.damageType != DamageType.Bomb)
// return;
if (OnDefense != null)
{
Debug.LogWarning($"{name} 가 {((HitCheck)data).owner.name} 의 공격을 방어");
System.Action<Character> OnDefenseTmp = OnDefense;
OnDefense = null;
OnDefenseTmp(((HitCheck)data).owner);
return;
}
//회피 스킬이 있는 경우, 회피율 확인
if (skill.Active("Hit", "Flee") != null)
{
Debug.LogWarning($"{name} 가 {hitCheck.owner.name} 의 공격을 회피");
}
else
{
Damaged_Process(hitCheck);
}
}
[Space(10)]
public SoundPlayer spHit;
public SoundPlayer spDie;
/// <summary>
/// 캐릭터가 받은 데미지를 적용합니다.
/// </summary>
/// <param name="attacker">공격한 캐릭터</param>
void Damaged_Process(HitCheck attacker)
{
if (spHit != null)
SoundManager.Instance.EffectPlay(spHit.gameObject);
Character owner = attacker.owner;
int damage = 0;
switch(attacker.damageType)
{
case DamageType.None:
damage = owner.CalculateATK();
break;
case DamageType.Fixed://고정 데미지
damage = (int)attacker.damageValue;
break;
case DamageType.Rate://배율 데미지
damage = (int)(owner.CalculateATK() * attacker.damageValue);
break;
case DamageType.RateHP://HP 비율
{
if (iff == IFF.Enemy)
damage = (int)(HP * attacker.damageValue);
else if (iff == IFF.Our)
damage = 1;//플레이어는 HP 방식이 다름
}
break;
case DamageType.Bomb://폭탄 데미지
{
if (iff == IFF.Enemy)
damage = (int)attacker.damageValue;
else if (iff == IFF.Our)
damage = 1;//플레이어는 HP 방식이 다름
}
break;
}
Damage_Added(ref damage);
//if (iff == owner.iff)
//{
// if (iff == IFF.Enemy)
// damage = 150;
// else if (iff == IFF.Our)
// damage = 1;//플레이어는 HP 방식이 다름
//}
Vector3 dir = -(attacker.transform.position - transform.position).normalized;
Damage_Apply(damage, owner, dir, 0, false, () =>
{
List<SkillData> list = owner.skill.SkillList_Trigger("HitEnd");
for (int i = 0; i < list.Count; i++)
{
if (list[i].Activate("HitEnd"))
{
switch (list[i].work)
{
case "StatusEffect":
{
statusEffect.Add(list[i]);
}
break;
}
}
}
});
}
void Damage_Added(ref int damage)
{
float mag = 1;
StatusEffect.Data data = statusEffect.Check("Freeze");//상태이상:빙결 효과 걸린 상태에서 맞게되면 추가 데미지
if (data != null)
mag += data.skill.value;
//Debug.LogWarning($"추가데미지 : {damage} * {mag}");
damage = (int)(damage * mag);
//Debug.LogWarning($"최종데미지 : {damage}");
}
public void Damage_Apply(int damage, Character attacker, Vector3 knockBackDir, float knockBackAddRate, bool MOTION_FORCE, System.Action OnEnd)
{
CalculateHP(damage);//데미지 적용
ec.Invoke((int)State.HP_Change, 0);
if (DieCheck())
{
NO_DAMAGE = true;
hitBox.gameObject.SetActive(false);
OnLocked(true);//죽었음 행동 정지
statusEffect.OnInitialized();//죽었을 경우, 상태이상 초기화
bool SKILL = skill.Active("Die", null, active =>
{
switch (active.work)
{
case "Resurrection"://몬스터 부활
{
motion.isSkill = true;
Restore_MaxRate(active.value);
motion.typeBefore = Motion.Type.AttackEnd;
motion.Play_Force(active.motion2, () =>
{
OnCollider(true);
motion.Play(Motion.Type.Idle, true);
Invoke("Resurrection", 0.5f);
});
}
break;
}
});
//캐릭터 죽음
if (OnDie != null && !SKILL)
{
if (spDie != null)
SoundManager.Instance.EffectPlay(spDie.gameObject);
OnCollider(false);
OnDie(this, Vector3.zero);
}
}
else
{
Hit_Start(durationHit);//히트 이팩트 실행
if (OnHit != null)
OnHit(attacker, knockBackDir, knockBackAddRate, false, () =>
{
if (OnEnd != null)
OnEnd();
});
}
}
/// <summary>
/// 스킬 등에 의해 부활했을 경우 초기화를 진행합니다
/// </summary>
public void Resurrection()
{
NO_DAMAGE = false;
motion.isSkill = false;
OnDefense = null;
hitBox.gameObject.SetActive(true);
gameObject.SetActive(true);
OnLocked(false);
OnResurrection(this);
}
[Space(10)]
public Motion motion;
[Space(10)]
public Skill skill;
[Space(10)]
public StatusEffect statusEffect;
[Space(5)]
public bool STUN = false;
[Space(10)]
public List<DropItemData> drops;
void DropInitialized()
{
for (int i = 0; i < drops.Count; i++)
drops[i].Get();
}
[System.Serializable]
public class LockEvent : UnityEvent<bool> { }
[FormerlySerializedAs("onLocked")]
[SerializeField]
[Space(10)]
private LockEvent onLocked = new LockEvent();
/// <summary>
/// 캐릭터의 행동을 제한 합니다.
/// </summary>
/// <param name="LOCK"></param>
public void OnLocked(bool LOCK)
{
onLocked.Invoke(LOCK);
}
[System.Serializable]
public class WarpEvent : UnityEvent<Vector3> { }
[FormerlySerializedAs("onWarp")]
[SerializeField]
[Space(10)]
private WarpEvent onWarp = new WarpEvent();
/// <summary>
/// 캐릭터를 해당 위치로 이동시킵니다.
/// </summary>
/// <param name="pos"></param>
public void OnWarp(Vector3 pos)
{
onWarp.Invoke(pos);
}
/// <summary>
/// 아이템을 습득했으므로 습득모션을 재생합니다.
/// </summary>
/// <param name="dropItem"></param>
/// <param name="OnEvent"></param>
public void OnGain(GameObject dropItem, System.Action OnEvent)
{
OnLocked(NO_DAMAGE = true);
motion.Play(Motion.Type.AttackEnd);
motion.Play(Motion.Type.Get, true, () =>
{
OnLocked(NO_DAMAGE = false);
}, ev =>
{
OnEvent();
}, true);
}
[Space(10)]
public SkeletonAnimation skeletonAnimation;
public Texture mainTexture;
public Material matOrigin;
[Space(5)]
public bool USE_HP30DOWN = true;
public float hpDownRate = 0.2f;
public ShaderControlValue scvHP30Down;
public Material matHP30Down;
Material matHP30Down_Copy;
/// <summary>
/// HP 의 % 에 따라 다른 효과를 적용합니다.
/// </summary>
public void Material_Restore()
{
if (USE_HP30DOWN && matHP30Down_Copy != null && HP < HP_Max * hpDownRate)
{
skeletonAnimation.CustomMaterialOverride[matOrigin] = matHP30Down_Copy;
scvHP30Down.Play(true, true, null);
}
else
{
skeletonAnimation.CustomMaterialOverride.Remove(matOrigin);
scvHP30Down.Play(false, false, null);
}
}
[Space(5)]
public Material matHit;
Material matHit_Copy;
public float durationHit = 0f;
[System.Serializable]
public class HitSyncEvent : UnityEvent<float> { }
[FormerlySerializedAs("onHitSynced")]
[SerializeField]
private HitSyncEvent onHitSynced = new HitSyncEvent();
[Space(5)]
public Material matHitResist;
Material matHitResist_Copy;
public bool HIT_RESIST = false;
public float durationHitResist = 0f;
public bool NO_DAMAGE = false;
[System.Serializable]
public class ResistSyncEvent : UnityEvent<float> { }
[FormerlySerializedAs("onResistSynced")]
[SerializeField]
private ResistSyncEvent onResistSynced = new ResistSyncEvent();
/// <summary>
/// 히트 이팩트를 시작합니다.
/// </summary>
/// <param name="duration"></param>
public void Hit_Start(float duration)
{
if (duration == 0)
return;
CancelInvoke("Hit_End");
skeletonAnimation.CustomMaterialOverride[matOrigin] = matHit_Copy;
if (HIT_RESIST)
NO_DAMAGE = true;
Invoke("Hit_End", duration);//히트 이팩트 종료
onHitSynced.Invoke(duration);//스파인, 어태치먼트의 경우 따로 적용해야함
}
/// <summary>
/// 히트 이팩트를 종료하고 원상태로 되돌립니다
/// </summary>
void Hit_End()
{
//skeletonAnimation.CustomMaterialOverride.Remove(matOrigin);
Material_Restore();
//무적상태 적용
if (HIT_RESIST)
{
skeletonAnimation.CustomMaterialOverride[matOrigin] = matHitResist_Copy;
Invoke("HitResist_End", durationHitResist);
onResistSynced.Invoke(durationHitResist);
}
}
/// <summary>
/// 무적상태를 종료합니다.
/// </summary>
void HitResist_End()
{
//skeletonAnimation.CustomMaterialOverride.Remove(matOrigin);
Material_Restore();
NO_DAMAGE = false;
}
[Space(10)]
public ShaderControlValue scvHide;
public Material matHide;
Material matHide_Copy;
/// <summary>
/// 캐릭터가 죽었으므로 숨김이팩트를 시작합니다.
/// </summary>
public void Hide_Start()
{
skeletonAnimation.CustomMaterialOverride[matOrigin] = matHide_Copy;
scvHide.Play();
}
public void Hide_End()
{
Debug.LogWarning($"Hide_End");
//skeletonAnimation.CustomMaterialOverride.Remove(matOrigin);
Material_Restore();
gameObject.SetActive(false);
}
[Space(10)]
public ShaderControlValue scvShow;
public Material matShow;
Material matShow_Copy;
/// <summary>
/// 캐릭터가 초기화 됐으면 맵에 나타나게 합니다.
/// </summary>
public void Show_Start()
{
return;
if (scvShow != null)
{
if (matShow != null)
{
if (matShow_Copy == null)
{
matShow_Copy = new Material(matShow);
matShow_Copy.SetTexture("_MainTex", mainTexture);
scvShow.Init(matShow_Copy);
}
skeletonAnimation.CustomMaterialOverride[matOrigin] = matShow_Copy;
scvShow.Play();
}
}
}
}