using Framework; using Gameplay.Buff; using Gameplay.Character; using Gameplay.Level; using System; using System.Collections.Generic; using cfg.BuffCfg; using CodeStage.AntiCheat.ObscuredTypes; using Cysharp.Threading.Tasks; using Gameplay.Area; using Gameplay.Bullet; using Gameplay.Common; using Gameplay.Common.Attri; using Gameplay.Pool; using Gameplay.Skill; using Gameplay.Unit.AI; using Gameplay.Unit.Data; using UnityEngine; namespace Gameplay.Unit { public class GameUnit : Framework.Wrapper.Unit { public bool isReady = false; public readonly EGameUnitType gameunitType; public EAvatarType avatarType { get; protected set; } /// /// 公用数据,或者其他一些不知道放哪的数据 /// [FightDebug] public readonly UnitCommonData commonData; /// /// logic移动相关数据,view层主要与此进行同步 /// [FightDebug] public readonly UnitTransData transData; /// /// 生存相关数据,控制角色的死亡,复活等 /// [FightDebug] public readonly UnitAliveData aliveData; /// /// 战斗相关数据,主要用于公式计算相关 /// [FightDebug] public readonly UnitFightData fightData; /// /// 战斗内的控制数据,主要用于战斗内的逻辑控制 /// [FightDebug] public readonly UnitControlData controlData; /// /// 状态数据,主要用于标识判定 /// [FightDebug] public readonly UnitStatusData statusData; public bool needRemove; /// /// gameunit的buff管理器 /// 因为需要通过buff来控制一些属性,所以需要在gameunit中管理buff /// public UnitBuffManager buffManager; public UnitAIManager aiManager; public DelayMoraleDamage delayMoraleDamage; public GameUnit(uint id, int troopId, EGameUnitType type) : base(id) { transData = new UnitTransData(this); commonData = new UnitCommonData(this); aliveData = new UnitAliveData(this); fightData = new UnitFightData(this); controlData = new UnitControlData(this); statusData = new UnitStatusData(this); aiManager = new UnitAIManager(this); delayMoraleDamage = new DelayMoraleDamage(this); avatarType = EAvatarType.Unknown; gameunitType = type; commonData.troopId = troopId; } public async UniTask Prepare() { await _OnPrepare(); Node.GameObject.SetActive(false); } protected virtual async UniTask _OnPrepare() { } /// /// 用于从备战席到战场的初始化 /// 添加到战场中,并进行ready,此时并没有真正进入战斗 /// public void Ready() { _OnReady(); Node.GameObject.SetActive(true); EventManager.Instance.Send(EventManager.EventName.LevelUnitBorn, this); } protected virtual void _OnReady() { } /// /// 进入战斗,真正的进入战斗,如果有相关全局技能,则会触发 /// public void EnterBattle() { _OnEnterBattle(); EventManager.Instance.Send(EventManager.EventName.INFIGHT_UNIT_READY, this); } protected virtual void _OnEnterBattle() { } /// /// 离开战斗,如果有相关全局技能,则会触发 /// public void ExitBattle() { Node.GameObject.SetActive(false); _OnExitBattle(); } protected virtual void _OnExitBattle() { } /// /// 回到备战席,通常需要隐藏模型一类的 /// public void BackPrepare() { _OnBackPrepare(); Node.GameObject.SetActive(false); } protected virtual void _OnBackPrepare() { } public void LogicUpdate(float dt) { _OnLogicUpdate(dt); if (statusData.isAIControll) { aiManager.LogicUpdate(dt); } } protected virtual void _OnLogicUpdate(float dt) { } public void SetPosAndYaw(int cellIndex, float yaw) { transData.disableEvent = true; // 设置数据 transData.pos = AreaManager.instance.GetCellPosByIndex(cellIndex); transData.yaw = yaw; // 设置view _node.GameObject.transform.position = transData.pos; _node.GameObject.transform.eulerAngles = new Vector3(0, yaw, 0); transData.disableEvent = false; } public void RecoverMorale(int value) { var finalValue = Mathf.FloorToInt(value * fightData.moraleRecoverScale); _OnRecoverMorale(finalValue); } protected virtual void _OnRecoverMorale(int value) { aliveData.currentMorale += value; if (aliveData.currentMorale > aliveData.maxMorale) { aliveData.currentMorale = aliveData.maxMorale; } } private bool _CheckIsUnbeatble() { if (SROptions.Current == null) return false; var isSelf = commonData.troopId == ETroopsId.Self; var isUnbeatable = isSelf ? SROptions.Current.SelfUnbeatable : SROptions.Current.EnemyUnbeatable; return isUnbeatable; } public void TakeMoraleDamage(int damage, GameUnit sender) { if (_CheckIsUnbeatble()) { return; } var baseDamage = damage; var damageScale = sender.fightData.moraleDamageScale; var beDamageScale = fightData.moraleBeDamageScale; var realDamage = Mathf.CeilToInt(baseDamage * damageScale * beDamageScale); // 特殊检查 var yalishuaijianBuffs = buffManager.GetBuffsByType(EBuffType.SpecialYaLiShuaiJian); if (yalishuaijianBuffs.Count != 0) { // 只去第一个,靠配表保证只有一个 var yalishuaijianBuff = yalishuaijianBuffs[0] as BuffSpecialYaLiShuaiJian; if (yalishuaijianBuff == null) { DebugUtil.LogError("不应该出现这个错误,说明类型转换出错"); return; } var decreaseRate = yalishuaijianBuff.decreasePercent; var decreseValue = Mathf.CeilToInt(decreaseRate * realDamage); realDamage -= decreseValue; // decrease的伤害,按秒给出去 delayMoraleDamage.AddSecondsDamage(decreseValue, yalishuaijianBuff.delayTime); } _OnTakeMoraleDamage(realDamage); } public void DamageMoraleImd(int damage) { aliveData.currentMorale -= damage; if (aliveData.currentMorale < 0) { aliveData.currentMorale = 0; } } protected virtual void _OnTakeMoraleDamage(int damage) { commonData.lastInFightSeconds = LevelManager.Instance.CurrentLevel.LevelTime; DamageMoraleImd(damage); } public void RecoverShield(int value, bool canOverFlow) { _OnRecoverShield(value, canOverFlow); } protected virtual void _OnRecoverShield(int value, bool canOverFlow) { if (canOverFlow) { aliveData.currentShield += value; var maxShiled = aliveData.maxShield + fightData.overflowShiledMaxValue; if (aliveData.currentShield > maxShiled) { aliveData.currentShield = maxShiled; } } else { if (aliveData.currentHp < aliveData.maxShield) { aliveData.currentShield += value; var maxShiled = aliveData.maxShield; if (aliveData.currentShield > maxShiled) { aliveData.currentShield = maxShiled; } } } } public void RecoverHp(int value) { _OnRecoverHp(value); } protected virtual void _OnRecoverHp(int value) { aliveData.currentHp += value; if (aliveData.currentHp > aliveData.maxHp) { aliveData.currentHp = aliveData.maxHp; } } public void TakeNormalDamage(ObscuredInt damage, GameUnit sender, bool isCritical) { commonData.lastInFightSeconds = LevelManager.Instance.CurrentLevel.LevelTime; commonData.lastBeHitSeconds = LevelManager.Instance.CurrentLevel.LevelTime; // 通用处理 var senderLevel = sender.commonData.level; if (sender.gameunitType == EGameUnitType.Vehicle) { // 载具进行攻击时, 等级强制为受击者当前等级 senderLevel = commonData.level; } var defense = fightData.defense; if (defense < 0) { defense = 0; } // 减伤值 var valueN = GLConfig.Inst.data.FPN; var valueK = GLConfig.Inst.data.FPK; var preCalc = (senderLevel - commonData.level) * valueN + defense + valueK; var reduceValue = 1f; if (preCalc > 0) { reduceValue = defense / preCalc; } var reduceScaler = 1 - reduceValue; var skillRate = sender.fightData.skillRate; var scaleForSpecial = 1f; switch (gameunitType) { case EGameUnitType.Character: scaleForSpecial = sender.fightData.scaleForHuman; break; case EGameUnitType.Vehicle: scaleForSpecial = sender.fightData.scaleForVehicle; break; } var finalDamageScale = sender.fightData.finalDamageScale; var beHitScale = fightData.beDamageScale; var hasShield = aliveData.currentShield > 0; var ignoreRandom = UnityEngine.Random.Range(0, 1f); var ignoreShieldRate = sender.fightData.ignoreShieldRate; var isignoreShield = ignoreRandom < ignoreShieldRate; if (isignoreShield) { hasShield = false; } var scaleAttack = hasShield ? sender.fightData.attackScaleForShiled : sender.fightData.attackScaleForHp; var realDamage = Mathf.CeilToInt(damage * skillRate * reduceScaler * scaleAttack * scaleForSpecial * finalDamageScale * beHitScale); if (realDamage < 1) { realDamage = 1; } _OnTakeNormalDamage(realDamage, sender, isCritical, isignoreShield); } protected virtual void _OnTakeNormalDamage(ObscuredInt realDamage, GameUnit sender, bool isCritical, bool ignoreShield) { var data = ObjPoolManager.instance.Get(); data.sender = sender; data.owner = this; data.damage = realDamage; data.isCritical = isCritical; EventManager.Instance.Send(EventManager.EventName.INFIGHT_GET_DAMAGED, data); ObjPoolManager.instance.Return(data); if (aliveData.currentShield > 0 && !ignoreShield) { if (aliveData.currentShield >= realDamage) { if (_CheckIsUnbeatble()) { return; } aliveData.currentShield -= realDamage; return; } else { if (_CheckIsUnbeatble()) { return; } realDamage -= aliveData.currentShield; aliveData.currentShield = 0; _DamageHP(realDamage); } } else { _DamageHP(realDamage); } } private void _DamageHP(int damage) { if (_CheckIsUnbeatble()) { return; } aliveData.currentHp -= damage; if (aliveData.currentHp < 0) { aliveData.currentHp = 0; } } public void OnSelect() { if (DebugUtil.LogEnable) DebugUtil.Log("Select: ----- GameUnit : {0}", GetID()); _OnSelect(); EventManager.Instance.Send(EventManager.EventName.LevelUnitSelect, this); } protected virtual void _OnSelect() { SelecterView.Instance.SetSelectAndTarget(Node, null); } public void OnUnSelect() { if (DebugUtil.LogEnable) DebugUtil.Log("UnSelect ---- GameUnit: {0}", GetID()); _OnUnSelect(); EventManager.Instance.Send(EventManager.EventName.LevelUnitUnSelect, this); } protected virtual void _OnUnSelect() { SelecterView.Instance.UnSelect(Node); } public void MoveAway(GameUnit unit) { Vector2Int targetPos; var map = LevelManager.Instance.CurrentLevel.Map; map.WorldPosition2BlockPosition(unit.Node.GetPosition(), out targetPos); var selfPos = map.TGSCellIndex2BlockPosition(transData.cellIndex); selfPos.x = Math.Clamp((selfPos.x - targetPos.x) > 0 ? selfPos.x + 1 : selfPos.x - 1, 0, map.MapData.Height - 1); selfPos.y = Math.Clamp((selfPos.y - targetPos.y) > 0 ? selfPos.y + 1 : selfPos.y - 1, 0, map.MapData.Width - 1); MoveTo(map.BlockPosition2TGSCellIndex(selfPos), true); } public void MoveTo(GameUnit unit) { if (unit == null) { DebugUtil.LogError("moveto的对象为空"); return; } controlData.followUnit = unit; MoveTo(unit.transData.cellIndex, false); } public void MoveTo(int cellIndex, bool cancelFollow) { if (cancelFollow) { controlData.followUnit = null; } var map = LevelManager.Instance.CurrentLevel.Map; if (map == null) return; controlData.targetCellIndex = cellIndex; if (LevelManager.Instance.CurrentLevel.selectUnit == this) { var wrapCellPos = map.TGSCellIndex2WorldPosition(cellIndex); SelecterView.Instance.SetSelectAndTarget(Node, new SelecterView.TargetInfo() { pos = wrapCellPos }); } } public void StopToCurrentCell() { controlData.targetCellIndex = transData.cellIndex; controlData.followUnit = null; } /// /// 该方法仅由skillhandle进行调用 /// /// /// public void _UseSkill(int cellIndex, SkillHandle skillHandle) { //test // PlayAudio(); _OnUseSkill(cellIndex, skillHandle); } private async void PlayAudio() { await AudioManager.Instance.PlayAudio("DemoProj_S1"); } protected virtual void _OnUseSkill(int cellIndex, SkillHandle skillHandle) { } /// /// AI自动使用技能 /// public void AITryUseSkill() { } public void EmitSkillBullet(int bulletId, GameUnit targetUnit, SkillHandle skillHandle) { commonData.lastInFightSeconds = LevelManager.Instance.CurrentLevel.LevelTime; var bullet = BulletManager.instance.EmitBullet(bulletId, targetUnit, skillHandle.owner); if (bullet != null) { if (statusData.needSkillPaused) { SkillManager.instance.screenEffect.AttachObj(bullet.rootObj); } } } public void EmitSkillBullets(int bulletId, List cellIndexs, SkillHandle skillHandle) { commonData.lastInFightSeconds = LevelManager.Instance.CurrentLevel.LevelTime; var skillConfig = skillHandle.runtimeData.configData; if (skillConfig.SelectCount < 0) { // 全选 for (var i = 0; i < cellIndexs.Count; ++i) { var cellIndex = cellIndexs[i]; var bullet = BulletManager.instance.EmitBullet(bulletId, cellIndex, skillHandle.owner); if (bullet != null) { if (statusData.needSkillPaused) { SkillManager.instance.screenEffect.AttachObj(bullet.rootObj); } } } } else { // 随机选择指定个数 var listAll = new List(); for (var i = 0; i < cellIndexs.Count; ++i) { var cellIndex = cellIndexs[i]; BulletManager.instance.PreSelectUnits(cellIndex, bulletId, skillHandle.owner, listAll); } List randomSelect = null; if (listAll.Count > skillConfig.SelectCount) { // 随机指定个数 randomSelect = PhxhSDK.Utils.GetRandomElementFromList(listAll, skillConfig.SelectCount); } else { randomSelect = listAll; } for (int i = 0; i < randomSelect.Count; i++) { var selectUnit = randomSelect[i]; var bullet = BulletManager.instance.EmitBullet(bulletId, selectUnit, skillHandle.owner); if (bullet != null) { if (statusData.needSkillPaused) { SkillManager.instance.screenEffect.AttachObj(bullet.rootObj); } } } } } } }