using Framework; using Framework.Wrapper; using Gameplay.Buff; using Gameplay.Character; using Gameplay.Level; using System; using System.Collections.Generic; using cfg.BuffCfg; using CodeStage.AntiCheat.ObscuredTypes; using Gameplay.Bullet; using Gameplay.Skill; using PhxhSDK; using UnityEngine; namespace Gameplay.Common { public class GameUnit : Unit { public bool isReady = false; public readonly EGameUnitType gameunitType; public EAvatarType avatarType { get; protected set; } /// /// 公用数据,或者其他一些不知道放哪的数据 /// public readonly UnitCommonData commonData; /// /// logic移动相关数据,view层主要与此进行同步 /// public readonly UnitTransData transData; /// /// 生存相关数据,控制角色的死亡,复活等 /// public readonly UnitAliveData aliveData; /// /// 战斗相关数据,主要用于公式计算相关 /// public readonly UnitFightData fightData; /// /// 战斗内的控制数据,主要用于战斗内的逻辑控制 /// public readonly UnitControlData controlData; /// /// 状态数据,主要用于标识判定 /// public readonly UnitStatusData statusData; /// /// gameunit的buff管理器 /// 因为需要通过buff来控制一些属性,所以需要在gameunit中管理buff /// public UnitBuffManager buffManager; 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); delayMoraleDamage = new DelayMoraleDamage(this); avatarType = EAvatarType.Unknown; gameunitType = type; commonData.troopId = troopId; EventManager.Instance.Send(EventManager.EventName.UnitCreate, this); } 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) { if (_CheckIsUnbeatble()) { return; } 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; // 减伤值 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) { EventManager.Instance.Send(EventManager.EventName.INFIGHT_GET_DAMAGED, new EventDataGetDamaged() { sender = sender, owner = this, damage = realDamage, isCritical = isCritical, }); if (aliveData.currentShield > 0 && !ignoreShield) { if (aliveData.currentShield >= realDamage) { aliveData.currentShield -= realDamage; return; } else { realDamage -= aliveData.currentShield; aliveData.currentShield = 0; _DamageHP(realDamage); } } else { _DamageHP(realDamage); } } private void _DamageHP(int damage) { 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; } 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) { } 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 = ArrayEx.RandomArrayFromArray(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); } } } } } } }