/* * @Author: 白哉 * @Description: 小兵逻辑 * @Date: 2025年04月01日 星期二 11:04:28 * @Modify: */ using System; using System.Collections.Generic; using System.Linq; using Animancer; using Logic.Audio; using Logic.CrashSight; using Logic.Event; using Logic.Skill; using Logic.Timeline; using UnityEngine; using RuntimeData; using TH1Renderer; namespace Logic { public enum DamageType { ActiveAttack, CounterAttack, FollowAttack, Splash, True, KillSelf, } public class SettlementInfo { //伤害类型 public DamageType DamageType; //伤害来源的unitId public UnitData DamageOrigin; //伤害目标的unitId public UnitData DamageTarget; public GridData DamageTargetGrid; //伤害值 public int DamageValue; //是否击杀 public bool IsKill; } public class UnitLogic : IUnitLogic { float[,] MoveInfoCostMap; float[,] MoveInfoMap; public UnitLogic() { MoveInfoCostMap = new float[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize]; MoveInfoMap = new float[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize]; } public void Update() { } //-------- 信息判断类 ---------// //判断uid是不是selfplayer的unit public bool CheckIsSelfPlayer(MapData mapData, uint uid) { mapData.GetPlayerDataByUnitId(uid, out var playerData); return playerData == mapData.PlayerMap.SelfPlayerData; } //-------- 执行类 --------// public bool MoveTo(MapData mapData, UnitData unitData, GridData gridData,MoveType moveType) { mapData.SetUnitIdToGridId(unitData.Id,gridData.Id); if (mapData == Main.MapData) AchievementDataManager.Instance.OnUnitMove(mapData, unitData, gridData); //如果主动移动会消耗所有类别的行动点数,被动挤出去的移动不算 if(moveType != MoveType.PassiveMove && moveType != MoveType.AttackMove ) unitData.MP = unitData.CP = unitData.AP = 0; //AUDIO 播放移动音效 if (mapData == Main.MapData) AudioManager.Instance.PlayAudio("SFX/UNIT_move"); //如果 land to port if (unitData.GetLandType() == LandType.LandAndPort && gridData.Resource == ResourceType.Port) { //注意,必须先Onmove(变身之前的技能用掉,再变身) unitData.OnMove(mapData, gridData,moveType); LandToBoat(mapData, unitData); } //如果sea to land else if (unitData.GetLandType() == LandType.WaterAndAshore && gridData.Terrain == TerrainType.Land) { //注意,必须先Onmove(变身之前的技能用掉,再变身) unitData.OnMove(mapData, gridData,moveType); BoatToLand(mapData, unitData); } //如果sea to sea或者land to land else //处理DASH等等技能情况,在Move结束时会触发的那些技能 unitData.OnMove(mapData, gridData,moveType); if (!mapData.GetPlayerDataByUnitId(unitData.Id, out var playerData)) { LogSystem.LogError("unit找不到player"); return false; } //如果是盟友的navalbase,且对方有恢复力,则消耗恢复力,恢复该单位所有行动点 if (gridData.Resource == ResourceType.NavalBase && mapData.GetPlayerDataByUnitId(unitData.Id, out var unitPlayer) && mapData.GetPlayerDataByTerritoryGridId(gridData.Id, out var gridPlayer) && mapData.SameUnion(unitPlayer.Id, gridPlayer.Id) && gridData.NavalBasePoint > 0) { gridData.NavalBasePoint--; unitData.AP = 1; unitData.CP = 1; unitData.MP = 1; unitData.RenderMark = true; } // 更新视野情况 int radius = unitData.GetSightRange(); if (gridData.Feature == TerrainFeature.Mountain) radius = radius < 2 ? 2 : radius; Main.PlayerLogic.UpdateSight(mapData,playerData,mapData.GridMap.GetAroundGridIdList(radius,gridData)); return true; } //返回unit2是否能反击unit1 public bool CanCounter(MapData mapData, UnitData unit1, UnitData unit2) { if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return false; if (!mapData.GetPlayerDataByUnitId(unit2.Id, out var player2)) return false; if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false; if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false; // 计算攻击伤害 int dmg1 = Table.Instance.CalcDamage(mapData, unit1, unit2); //设置unit1 attackendermark和相关参数 //判断对方能否反击的参数 bool canCounter; canCounter = true; //是否限制敌方反击 if (unit1.IsLimitTargetCounterAttack(mapData)) canCounter = false; //敌方是否被限制反击 if (unit2.IsLimitSelfCounterAttack(mapData)) canCounter = false; //确认对方是否有我的视野,没有的话无法反击 if (!player2.Sight.CheckIsInSight(grid1.Id)) canCounter = false; //如果对方攻击范围无法覆盖我,则无法反击 if (Table.Instance.CalcDistance(new Vector2Int(grid1.Pos.X,grid1.Pos.Y),new Vector2Int(grid2.Pos.X,grid2.Pos.Y)) > unit2.GetAttackRange()) canCounter = false; if (dmg1 >= unit2.Health) //如果伤害直接够杀死对方 canCounter = false; return canCounter; } //返回unit1攻击unit2的动画全长度 public float AttackAnimTime(MapData mapData, UnitData unit1, UnitData unit2) { if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return 0f; if (!mapData.GetPlayerDataByUnitId(unit2.Id, out var player2)) return 0f; if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return 0f; if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return 0f; // 计算攻击伤害 int dmg1 = Table.Instance.CalcDamage(mapData, unit1, unit2); //判断对方能否反击的参数 bool canCounter = CanCounter(mapData,unit1,unit2); //第一次攻击到对方的时刻 float attackWait = Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit1.GetAttackRange()); //第一次攻击到对方后返回的时刻 float attackBack = attackWait + Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange(unit1.GetAttackRange()); float counterAttackStart = attackBack+ Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime; //第一次攻击到对方后返回+对方反击打到我的时刻 float counterAttackWait = attackBack + Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit2.GetAttackRange()) + Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime; //第一次攻击到对方后返回+对方反击打到我+对方返回的时刻 float counterAttackBack = counterAttackWait + Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange( unit2.GetAttackRange()); if (dmg1 >= unit2.Health) //如果伤害直接够杀死对方 { //攻击+移动:如果是近战且目标位置不是山或者有山的科技,且目标不在水里 if (unit1.GetAttackRange() == 1 && grid2.Terrain == TerrainType.Land && (grid2.Feature != TerrainFeature.Mountain || player1.TechTree.CheckIfHasTech(TechType.Climbing))) return Table.Instance.AnimDataAssets.MoveAnimTime; else return attackBack; } //如果杀不死对方且不会被反击 else if(!canCounter) return attackBack; //否则就是完整的攻击时长 return counterAttackBack; } public bool Attack(MapData mapData, UnitData unit1, UnitData unit2, out float duration, bool isMoment=false) { duration = 0; if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return false; if (!mapData.GetPlayerDataByUnitId(unit2.Id, out var player2)) return false; if (!mapData.GetCityDataByUnitId(unit1.Id, out var city1)) return false; if (!mapData.GetCityDataByUnitId(unit2.Id, out var city2)) return false; if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false; if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false; if (unit1.IsLimitSelfAttack(mapData)) return false; Main.PlayerLogic.SetDiplomacyLeague(player1, player2, DiplomacyState.War); player1.CurAttackPlayers.Add(player2.Id); player2.CurAttackPlayers.Add(player1.Id); player2.AddAttacker(player1.Id); player1.TurnNoAttack = 0; // 计算攻击伤害 int dmg1 = Table.Instance.CalcDamage(mapData, unit1, unit2); int dmg2 = 0; //判断对方能否反击的参数 bool canCounter = CanCounter(mapData,unit1,unit2); //计算反击伤害,要给calcDamage额外传入一个true TODO 这里设计可以优化,方法最好拆分 if (canCounter) dmg2 = Table.Instance.CalcDamage(mapData, unit1, unit2, true); //攻击会消耗所有类别的行动点数 unit1.AP = unit1.CP = unit1.MP = 0; unit1.RenderMark = true; //设置unit1 attackendermark和相关参数 unit1.AttackRenderMark = true; unit1.AttackRenderMarkAttackAnimType = unit1.GetAttackRange() switch { 1 => AttackAnimType.Melee, 2 => AttackAnimType.Arrow, 3 => AttackAnimType.Bomb, _ => AttackAnimType.None }; if (unit1.GiantType == GiantType.EgyptianPatchouli) unit1.AttackRenderMarkAttackAnimType = AttackAnimType.PatchouliAttack; /*if (unit1.GiantType == GiantType.EgyptianRemilia) unit1.AttackRenderMarkAttackAnimType = AttackAnimType.RemiliaAttack;*/ unit1.AttackRenderMarkTargetPos = Table.Instance.GridToWorld(grid2,"isUnit"); unit1.AttackRenderMarkNeedBack = true; //一组参数用来处理攻击时长的 //第一次攻击到对方的时刻 float attackWait = Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit1.GetAttackRange()); //第一次攻击到对方后返回的时刻 float attackBack = attackWait + Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange(unit1.GetAttackRange()); float counterAttackStart = attackBack+ Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime; //第一次攻击到对方后返回+对方反击打到我的时刻 float counterAttackWait = attackBack + Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit2.GetAttackRange()) + Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime; //第一次攻击到对方后返回+对方反击打到我+对方返回的时刻 float counterAttackBack = counterAttackWait + Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange( unit2.GetAttackRange()); if (TestKillUnit(mapData,unit1,unit2,dmg1)) //如果伤害直接够杀死对方 { //攻击+移动:如果是近战且目标位置可以移动过去 if (unit1.GetAttackRange() == 1 && CheckUnitCanMoveToGrid(mapData,player1,unit1,grid2)) { unit1.AttackRenderMark = false; var fragment = new MoveKill(); fragment.Map = mapData; fragment.Duration = Table.Instance.AnimDataAssets.MoveAnimTime; fragment.Origin = unit1; fragment.Target = unit2; fragment.Dmg = dmg1; fragment.OriginGrid = grid1; fragment.TargetGrid = grid2; fragment.OriginPlayer = player1; fragment.TargetPlayer = player2; duration = fragment.Duration; if (isMoment) fragment.Excute(); else TimelineManager.Instance.AddFragment(fragment); } else { var fragment = new NotMoveKill(); fragment.Map = mapData; fragment.Duration = attackBack; fragment.Origin = unit1; fragment.Target = unit2; fragment.Dmg = dmg1; fragment.OriginGrid = grid1; fragment.TargetGrid = grid2; fragment.OriginPlayer = player1; fragment.TargetPlayer = player2; fragment.AttackTime = attackWait; fragment.AttackBackTime = attackBack; duration = fragment.Duration; if (isMoment) fragment.Excute(); else TimelineManager.Instance.AddFragment(fragment); } } //如果杀不死对方 else { var fragment = new AttackAndCounter(); fragment.Map = mapData; fragment.Duration = counterAttackBack; fragment.Origin = unit1; fragment.Target = unit2; fragment.Dmg1 = dmg1; fragment.Dmg2 = dmg2; fragment.OriginGrid = grid1; fragment.TargetGrid = grid2; fragment.OriginPlayer = player1; fragment.TargetPlayer = player2; fragment.CanCounter = canCounter; fragment.AttackAnimTime = Table.Instance.AnimDataAssets.AttackAnimTime; fragment.CounterAttackStart = counterAttackStart; fragment.CounterAttackWait = counterAttackWait; fragment.CounterAttackBack = counterAttackBack; duration = fragment.Duration; if (isMoment) fragment.Excute(); else TimelineManager.Instance.AddFragment(fragment); } return true; } // 伤害新结算方法,所有的掉血都要走此方法,不允许直接修改UnitData.Health // TODO 自杀或者杀队友要不要加 player.TotalKill // TODO 死亡动画标记移除了,最好用新系统实现 public SettlementInfo DamageSettlement(MapData mapData, UnitData origin, UnitData target, int dmg, DamageType type) { if (origin == null || target == null) { LogSystem.LogError($"DamageSettlement origin:{origin} or target{target} is null"); return null; } if (!mapData.GetGridDataByUnitId(target.Id, out var targetGrid)) { LogSystem.LogError($"Target Grid is null target.id:{target.Id}"); return null; } if (!target.CanBeDamaged(mapData, dmg)) return null; var settlement = new SettlementInfo(); settlement.DamageType = type; settlement.DamageOrigin = origin; settlement.DamageTarget = target; settlement.DamageValue = dmg; settlement.DamageTargetGrid = targetGrid; target.Health -= dmg; if (type == DamageType.KillSelf) { UnitDie(mapData, target, dmg); //ViewViewView 处理Die之后,周围3格内单位unit的rendermark,更新他们的高亮逻辑 MapRenderer.Instance.UpdateAroundAfterUnitDie(mapData,targetGrid); settlement.IsKill = true; } else if (target.CanBeKilled(mapData) && target.Health <= 0) { if (Main.MapData == mapData) AchievementDataManager.Instance.OnKillUnit(mapData, origin, target); UnitDie(mapData, target, dmg); //ViewViewView 处理Die之后,周围3格内单位unit的rendermark,更新他们的高亮逻辑 MapRenderer.Instance.UpdateAroundAfterUnitDie(mapData,targetGrid); settlement.IsKill = true; if (!origin.IsExpLock(mapData)) origin.Exp++; mapData.GetPlayerDataByUnitId(origin.Id, out var player); if (player != null) player.TotalKill++; } else { settlement.IsKill = false; } foreach (var skill in origin.Skills) skill.OnDamageOther(mapData, settlement); foreach (var skill in target.Skills) skill.OnDamaged(mapData, settlement); mapData.OnUnitDamaged(settlement); return settlement; } public void UnitDie(MapData map, UnitData unit, int dmg) { unit.Health = 0; if (!map.GetGridDataByUnitId(unit.Id, out var targetGrid)) return; //如果是真map,才播放动画 if(map == Main.MapData) DieAnimRenderMarkUpadate(targetGrid, unit, dmg,true); //如果是伟人死亡,对应的player受到惩罚 penalty=4 if (unit.UnitType == UnitType.Giant && map.GetPlayerDataByUnitId(unit.Id, out var player) && Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, unit.UnitLevel, out var info)) player.giantPenalty[(uint)info.ChessType] = 4; Main.UnitLogic.Die(map, unit); } public void DieAnimRenderMarkUpadate(GridData grid2,UnitData unit2,int dmg,bool needRandomWait = false) { //播放grid2的hurtVFX MapRenderer.Instance.ROGridMap[grid2.Id].SetBounceAnim(NeedRandomWait:needRandomWait); grid2.VFXRenderMarkHurt = true; //播放grid2上的伤害数字 var t = new GridVFXRenderMark(GridVFXType.Damage); t.Damage = dmg; grid2.SetGridVFXRenderMark(t); //播放grid2上的死亡特效 grid2.VFXRenderMarkDie = true; //播放grid2上的死亡雾效 grid2.VFXRenderMarkFog = true; //更新unit2的信息 unit2.RenderMark = true; } public void DamageAnimRenderMarkUpdate(GridData grid2,UnitData unit2,int dmg,bool needRandomWait = false) { //处理动画效果 MapRenderer.Instance.ROGridMap[grid2.Id].SetBounceAnim(NeedRandomWait:needRandomWait); grid2.VFXRenderMarkHurt = true; var t = new GridVFXRenderMark(GridVFXType.Damage); t.Damage = dmg; grid2.SetGridVFXRenderMark(t); //更新unit2的信息 unit2.RenderMark = true; } public bool TestKillUnit(MapData mapData, UnitData origin, UnitData target, int dmg) { if (dmg < target.Health) return false; if (!target.CanBeKilled(mapData)) return false; return true; } public void DebugOutputMoveInfo(MapData mapData) { string t = "\n"; for (int i = (int)mapData.MapConfig.Width - 1; i >= 0; i--) { for (int j = (int)mapData.MapConfig.Height - 1; j >= 0; j--) if (MoveInfoMap[i, j] < 0) t += "@"; else t += ((int)MoveInfoMap[i, j]).ToString(); t += "\n"; } Debug.Log(t); } // 判断我方单位是否可以站在某个格子上, 不考虑视野信息,不考虑可达 public bool CheckUnitCanStandOnGrid(MapData map, PlayerData self,UnitData unit ,GridData grid) { //如果unit不是null,那么就是判断具体的某个unit能否站在grid上 if (unit != null) { if(!unit.IsCanMoveOnTerrain(map,grid.Terrain))return false; if(!unit.IsCanMoveOnFeature(map, grid.Feature))return false; if (grid.Terrain == TerrainType.ShallowSea && grid.Resource != ResourceType.Port) return false; if (grid.Terrain == TerrainType.ShallowSea && grid.Resource == ResourceType.Port) { if (!map.GetPlayerDataByUnitId(unit.Id, out var playerA)) return false; if (!map.GetPlayerDataByTerritoryGridId(grid.Id,out var playerB)) return false; if (!map.SameUnion(playerA.Id, playerB.Id)) return false; } } //否则可能是判断一个玩家的任意unit能否站在上面(也就是玩家整个科技树是否支持站在上面) else { if (grid.Feature == TerrainFeature.Mountain && !self.TechTree.CheckIfHasTech(TechType.Climbing)) return false; if (grid.Terrain == TerrainType.ShallowSea && !self.TechTree.CheckIfHasTech(TechType.Fishing)) return false; if (grid.Terrain == TerrainType.DeepSea && !self.TechTree.CheckIfHasTech(TechType.Sailing)) return false; } return true; } // 仅从feature和terrain角度判断我方单位是否可以移动到某个格子上, 不考虑视野信息 //TODO 整理这一块代码,后续用这个来判断 public bool CheckUnitCanMoveToGrid(MapData map, PlayerData self, UnitData unit, GridData grid ) { var landType = unit.GetLandType(); //step #1 排除科技情况 if (grid.Feature == TerrainFeature.Mountain && !self.TechTree.CheckIfHasTech(TechType.Climbing)) return false; if (grid.Terrain == TerrainType.ShallowSea && !self.TechTree.CheckIfHasTech(TechType.Fishing)) return false; if (grid.Terrain == TerrainType.DeepSea && !self.TechTree.CheckIfHasTech(TechType.Sailing)) return false; //step #2 排除landandport单位的情况 if (landType == LandType.LandAndPort) { if (grid.Terrain == TerrainType.DeepSea) return false; if (grid.Terrain == TerrainType.ShallowSea && (grid.Resource != ResourceType.Port || !map.CheckIfGidBelongPidUnion(grid.Id,self.Id)))return false; } //step #3 if (landType == LandType.WaterAndAshore) { } return true; } //TODO 这个是不是要更新一下,用上面的函数 public bool CheckUnitCanMoveToGrid(MapData map, PlayerData self, Vector2Int start, Vector2Int end) { if (!map.GridMap.GetGridDataByPos(start.x, start.y, out var startGrid)) return false; if (!map.GridMap.GetGridDataByPos(end.x, end.y, out var endGrid)) return false; if (endGrid.Feature == TerrainFeature.Mountain && !self.TechTree.CheckIfHasTech(TechType.Climbing)) return false; if (endGrid.Terrain == TerrainType.DeepSea && !self.TechTree.CheckIfHasTech(TechType.Sailing)) return false; //这条没懂 if (endGrid.Terrain == TerrainType.DeepSea && startGrid.Terrain != TerrainType.ShallowSea) return false; if (endGrid.Terrain == TerrainType.ShallowSea && startGrid.Terrain == TerrainType.Land && endGrid.Resource != ResourceType.Port) return false; return true; } //计算单位移动或者攻击的网格数组,存储在InfoMap中,方便后续判断使用,是一个需要立刻使用的,非长久存储的临时infomap public void CalcUnitMoveInfo(MapData mapData, uint uid) { mapData.UnitMap.GetUnitDataByUnitId(uid, out var unitData); mapData.GetPlayerDataByUnitId(uid, out var playerData); mapData.GetGridDataByUnitId(uid, out var gridData); int width = (int)mapData.MapConfig.Width; int height = (int)mapData.MapConfig.Height; //根据地形先设置好costMap基础信息,MoveInfoCostMap = -1表示不可抵达 foreach (var targetGridData in mapData.GridMap.GridList) { int i = targetGridData.Pos.X, j = targetGridData.Pos.Y; if (playerData.Sight.CheckIsInSight(targetGridData.Id)) //如果是陆地单位 if (unitData.GetLandType() == LandType.LandAndPort) { MoveInfoCostMap[i, j] = 1; if (targetGridData.Vegetation == Vegetation.Trees) MoveInfoCostMap[i, j] = 2; if (targetGridData.Feature == TerrainFeature.Mountain) MoveInfoCostMap[i, j] = playerData.TechTree.CheckIfHasTech(TechType.Climbing) ? 3 : -1; if (targetGridData.Terrain == TerrainType.ShallowSea) { if (!playerData.TechTree.CheckIfHasTech(TechType.Fishing)) MoveInfoCostMap[i, j] = -1; //如果是port,要判断是盟军的port 还是其他的port else if (targetGridData.Resource == ResourceType.Port) { if(mapData.GetPlayerDataByTerritoryGridId(targetGridData.Id,out var targetGridPlayer) && targetGridPlayer.Id == playerData.Id) MoveInfoCostMap[i, j] = 999; else MoveInfoCostMap[i, j] = -1; } else if (targetGridData.Resource == ResourceType.Bridge) MoveInfoCostMap[i, j] = 1; else MoveInfoCostMap[i, j] = -1; } if (targetGridData.Terrain == TerrainType.DeepSea) MoveInfoCostMap[i, j] = -1; } //如果是水上单位 else { if (targetGridData.Terrain == TerrainType.DeepSea) MoveInfoCostMap[i, j] = playerData.TechTree.CheckIfHasTech(TechType.Sailing) ? 1 : -1; else if (targetGridData.Terrain == TerrainType.ShallowSea) MoveInfoCostMap[i, j] = 1; else { MoveInfoCostMap[i, j] = 999; if (targetGridData.Feature == TerrainFeature.Mountain && !playerData.TechTree.CheckIfHasTech(TechType.Climbing)) MoveInfoCostMap[i, j] = -1; } } //没有视野,那就不可抵达 else MoveInfoCostMap[i, j] = -1; } //将所有敌人的控制区的costMap设置为999 foreach (UnitData B in mapData.UnitMap.UnitList) { if (!B.Alive) continue; mapData.GetPlayerDataByUnitId(B.Id, out var playerDataB); mapData.GetGridDataByUnitId(B.Id, out var gridDataB); if (playerDataB == playerData) continue; var targetGridDataList = mapData.GridMap.GetAroundGridDataSet(1,1,gridDataB); foreach (var targetGridData in targetGridDataList) { int x = targetGridData.Pos.X, y = targetGridData.Pos.Y; MoveInfoCostMap[x, y] = (MoveInfoCostMap[x, y] < 0) ? -1 : 999; } } //初始化infomap,<0表示不可达,<0.1则不再扩展,>0.1表示可以向附近扩展 for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) MoveInfoMap[i, j] = -1; MoveInfoMap[gridData.Pos.X,gridData.Pos.Y] = unitData.GetMoveRange(); //然后进行SPFA更新最短路 Queue q = new Queue(); //breakTime用来放置负边死循环情况 int breakTime = Mathf.Min(10000000, width * height * width * height); q.Enqueue(gridData.Id); while (breakTime > 0 && q.Count > 0) { breakTime--; if (!mapData.GridMap.GetGridDataByGid(q.Dequeue(), out var gridDataX)) continue; if (MoveInfoMap[gridDataX.Pos.X, gridDataX.Pos.Y] < 0.1f) continue; var nearby = mapData.GridMap.GetAroundGridDataSet(1,1,gridDataX); foreach (var gridDataY in nearby) { float cost = MoveInfoCostMap[gridDataY.Pos.X, gridDataY.Pos.Y]; //如果不可达区 if (cost <= 0)continue; //如果是陆地单位,看是否需要减去道路或者桥梁的行动力加成。道路是非敌人领地才行,并且目标不是耗光所有cost的港口或者敌占区 if (unitData.GetLandType() == LandType.LandAndPort && Main.PlayerLogic.HasRoadForUnit(mapData, gridDataX, gridDataY, unitData) && cost < 900) cost = 0.5f; //cost -= 0.5f + (gridDataY.Vegetation == Vegetation.Trees ? 1f : 0f); //如果能够发生更新 if (Mathf.Max(MoveInfoMap[gridDataX.Pos.X, gridDataX.Pos.Y] - cost, 0f) > MoveInfoMap[gridDataY.Pos.X, gridDataY.Pos.Y]) { MoveInfoMap[gridDataY.Pos.X, gridDataY.Pos.Y] = Mathf.Max(MoveInfoMap[gridDataX.Pos.X, gridDataX.Pos.Y] - cost, 0f); //如果xy不再队列里,并且还剩余有行动力,就加入队列 if (!q.Contains(gridDataY.Id) && MoveInfoMap[gridDataY.Pos.X, gridDataY.Pos.Y] > 0.1f) q.Enqueue(gridDataY.Id); } } } //检测有没有特别离谱的MoveInfo for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) if(MoveInfoMap[i, j] >= 0 && Table.Instance.CalcDistance(new Vector2Int(gridData.Pos.X,gridData.Pos.Y),new Vector2Int(i,j)) > 5) Debug.Log("moveinfo计算时出现了超过5格的移动距离"); //处理ALLTRANSPORT技能,可以移动到己方伟人的身周 if (unitData.IsCanMoveGiantNearbyGrid(mapData)) { //遍历所有单位,找到己方的伟人 foreach (var unitA in mapData.UnitMap.UnitList) if(unitA.UnitType == UnitType.Giant || unitA.CarryUnitType == UnitType.Giant && mapData.GetPlayerIdByUnitId(unitA.Id,out var pid) && pid == playerData.Id && unitA.Id != unitData.Id) { mapData.GetGridDataByUnitId(unitA.Id, out var gridB); var nearby = mapData.GridMap.GetAroundGridDataSet(1,1,gridB); foreach (var gridC in nearby){ int xx = gridC.Pos.X, yy = gridC.Pos.Y; if(CheckUnitCanStandOnGrid(mapData,playerData,unitData,gridC) && !mapData.GetUnitDataByGid(gridC.Id,out var tt)) { int x = gridC.Pos.X, y = gridC.Pos.Y; MoveInfoMap[x, y] = 0; }} } } //接入CITYTRANSPORT,在首都联通的城市之间传送 if (unitData.IsCanMoveNoUnitSelfCity(mapData)) { //当前在我方的首都或者首都联通的城市才可以 if (mapData.GetCityDataByGid(gridData.Id, out var cityA) && (cityA.IsCapital || cityA.IsConnectedCapital) && mapData.GetPlayerDataByCityId(cityA.Id,out var cityAsPlayer) && cityAsPlayer.Id == playerData.Id) { //遍历所有我方城市 如果位置没有人且联通,就可以移动过去 var cityDataList = new List(); mapData.GetCityDataListByPlayerId(playerData.Id, cityDataList); foreach (var cityB in cityDataList) { if(cityB.Id == cityA.Id)continue; if (!cityB.IsConnectedCapital && !cityB.IsCapital) continue; if (!mapData.GetGridDataByCityId(cityB.Id, out var gridB)) continue; if (mapData.GetUnitDataByGid(gridB.Id, out var _)) continue; MoveInfoMap[gridB.Pos.X, gridB.Pos.Y] = 0; } } } } // 检查单位是否能攻击目标点 public bool CheckUnitCanAttackPos(MapData mapData, UnitData unitData, GridData gridData) { if (Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info) && info.Attack <= 0) return false; if (!mapData.GetGridDataByUnitId(unitData.Id, out var originGrid)) return false; if (!mapData.GetUnitDataByGid(gridData.Id, out var targetUnit)) return false; if(!mapData.GetPlayerDataByUnitId(unitData.Id, out var player)) return false; if(!mapData.GetPlayerDataByUnitId(targetUnit.Id, out var targetPlayer)) return false; if (player == targetPlayer) return false; if (mapData.GridMap.CalcDistance(gridData, originGrid) > unitData.GetAttackRange()) return false; return true; } // None, Move, Attack, Port, MoveAshore public MoveAttackType CheckUnitCanMoveOrAttack(MapData mapData, UnitData unitDataA, GridData gridDataB) { //默认在调用这个函数前已经调用了 CalcUnitMoveInfo(A),计算好了。不组合使用是不行的 mapData.GetGridDataByUnitId(unitDataA.Id,out var gridDataA); //如果目标位置有单位 if(mapData.GetUnitDataByGid(gridDataB.Id,out var unitDataB)) { mapData.GetPlayerDataByUnitId(unitDataA.Id, out var playerDataA); mapData.GetPlayerDataByUnitId(unitDataB.Id, out var playerDataB); //如果目标位置上面有友军单位,那不可移动也不可攻击 if (playerDataA == playerDataB) return MoveAttackType.None; //如果目标位置上面有敌军单位,那不可移动,但是有可能可以攻击 else if (mapData.GridMap.CalcDistance(gridDataA, gridDataB) <= unitDataA.GetAttackRange() && Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitDataA.UnitType, unitDataA.GiantType,unitDataA.UnitLevel, out var info) && info.Attack > 0) //判断距离在攻击距离范围内,且攻击大于0,就可以攻击 { if(mapData.GridMap.CalcDistance(gridDataA, gridDataB) > 3) Debug.Log("异常的攻击距离"); return MoveAttackType.Attack; //返回可以攻击 } else //不在攻击范围内,不可以攻击 return MoveAttackType.None; } //如果目标位置没有任何单位 else { if (MoveInfoMap[gridDataB.Pos.X, gridDataB.Pos.Y] < 0f) //去不了直接返回 return MoveAttackType.None; if (gridDataB.Terrain == TerrainType.ShallowSea && unitDataA.GetLandType() == LandType.LandAndPort) //下港口 return MoveAttackType.MoveToPort; if (gridDataB.Terrain == TerrainType.Land && unitDataA.GetLandType() == LandType.WaterAndAshore) //上岸 return MoveAttackType.MoveAshore; return MoveAttackType.Move; } return MoveAttackType.None; } //判断当前 unit 附近有多少个单位可以攻击 public int AttackTargetCount(MapData mapData, UnitData unitData) { if (!mapData.GetGridDataByUnitId(unitData.Id, out var gridData)) return 0; int ret = 0; int r = Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,unitData.UnitLevel,out var info) ? info.AttackRange : 0; var aroundGridList = mapData.GridMap.GetAroundGridData(r, r, gridData); foreach (var aroundGrid in aroundGridList) { if (CheckUnitCanAttackPos(mapData, unitData, aroundGrid)) ret++; } return ret; } // 找某个点位的 Unit public UnitData FindUnitOnPos(MapData mapData, GridData gridData) { mapData.GetUnitDataByGid(gridData.Id, out var unitData); return unitData; } public void Upgrade(MapData mapData, UnitData u) { /* u.veteran = true; u.health = u.maxHealth + 5; RenderUpdateManager.Instance.AddUnitRUList(u.unitId); */ } public void AddMPAPCP(MapData mapData, UnitData unitData) { unitData.MP = 1; unitData.AP = 1; unitData.CP = 1; } //判断该unit是不是selfPlayer的unit //unit[uid]原地休息 public void Recover(MapData mapData, UnitData unitData) { } //unit[uid]进阶成为shipType类型的单位 public void TransformToShip(MapData mapData, UnitData unitData, UnitType shipType) { } //unit[uid]从陆地进入港口,变为boat public void LandToBoat(MapData mapData, UnitData unitData) { unitData.CarryUnitType = unitData.UnitType; unitData.CarryGiantType = unitData.GiantType; unitData.CarryUnitLevel = unitData.UnitLevel; if (unitData.UnitType == UnitType.BigGuy) Main.UnitLogic.UnitTypeTransform(mapData, unitData, UnitType.Juggernaut); else if(unitData.UnitType == UnitType.Giant) Main.UnitLogic.UnitTypeTransform(mapData, unitData, UnitType.GiantJuggernaut,GiantType.None,unitData.UnitLevel); else Main.UnitLogic.UnitTypeTransform(mapData, unitData, UnitType.Boat); unitData.AP = 0; unitData.MP = 0; unitData.CP = 0; unitData.CarryExp = unitData.Exp; unitData.Exp = 0; unitData.CarryVeteran = unitData.Veteran; unitData.Veteran = false; //unitData.MoveRange = Table.Instance.UnitInfoDict[UnitType.Boat].MoveRange; //unitData.AttackRange = Table.Instance.UnitInfoDict[UnitType.Boat].AttackRange; unitData.RenderMark = true; } //unit[uid]从港口进入陆地,变为原来的单位 public void BoatToLand(MapData mapData, UnitData unitData) { if (!mapData.GetPlayerDataByUnitId(unitData.Id, out var player) ||!mapData.GetGridDataByUnitId(unitData.Id,out var grid)) return; Main.PlayerLogic.UpdateSight(mapData,player,mapData.GridMap.GetAroundGridIdList(unitData.GetSightRange(),grid)); Main.UnitLogic.UnitTypeTransform(mapData,unitData, unitData.CarryUnitType, unitData.CarryGiantType,unitData.CarryUnitLevel); unitData.AP = 0; unitData.MP = 0; unitData.CP = 0; unitData.Exp = unitData.CarryExp; unitData.Veteran = unitData.CarryVeteran; unitData.RenderMark = true; } //unit[uid]死亡 public void Die(MapData mapData, UnitData unitData) { mapData.SetUnitDataDie(unitData); } //unitData开启下一回合 public void StartNextTurn(MapData mapData, UnitData unitData) { if (!unitData.Alive) return; unitData.AP = 1; unitData.MP = 1; unitData.CP = 1; unitData.RenderMark = true; } public void UnitEndTurn(MapData mapData, UnitData unitData) { if (!unitData.Alive) return; //在进入下一个回合前,如果可以回血自动回血 if (unitData.Health < unitData.GetMaxHealth() && unitData.CP > 0) { if (mapData.GetGridDataByUnitId(unitData.Id, out var grid) && mapData.GetPlayerDataByUnitId(unitData.Id, out var player) ) { unitData.Health += 2; //如果是自家或者盟军领土 if (mapData.CheckGridIdBelongPlayerIdUnion(grid.Id, player.Id)) unitData.Health += 2; if (unitData.Health > unitData.GetMaxHealth()) unitData.Health = unitData.GetMaxHealth(); grid.VFXRenderMarkHeal = true; unitData.RenderMark = true; } } } //unitData转换成另一个UnitType public void UnitTypeTransform(MapData mapData, UnitData unitData, UnitType targetType, GiantType giantType = GiantType.None,uint unitLevel = 0) { Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType, unitData.GiantType,unitData.UnitLevel, out var originInfo); Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(targetType, giantType,unitLevel, out var targetInfo); if (originInfo == null || targetInfo == null) return; foreach (var skilltype in originInfo.Skills) { //某些skill被Remove了,要做特殊处理情况 if (unitData.GetSkill(skilltype, out var skill)) skill.OnTransformRemoved(unitData,mapData); unitData.RemoveSkill(skilltype); } foreach (var skilltype in targetInfo.Skills) { //某些skill被Add了,要做特殊处理 if (unitData.GetSkill(skilltype, out var skill)) skill.OnTransformRemoved(unitData,mapData); unitData.AddSkill(skilltype); } unitData.UnitType = targetType; unitData.GiantType = giantType; unitData.UnitLevel = unitLevel; } public void PassiveMoveAway(MapData mapData,UnitData unitData) { if (!mapData.GetGridDataByUnitId(unitData.Id, out var gridData)) return; var gridList = mapData.GridMap.GetAroundGridData(1,1,gridData); foreach (var targetGrid in gridList) { if (targetGrid == gridData) continue; if (!CheckUnitCanGoOnGrid(mapData, unitData, targetGrid)) continue; //传一个true,表示被动移动,不会扣除MPAPCP MoveTo(mapData,unitData,targetGrid,MoveType.PassiveMove); return; } Die(mapData,unitData); } public bool CheckUnitCanGoOnGrid(MapData mapData, UnitData unitData, GridData gridData) { //TODO 这里一定要改了,用把landType通过unitSkill改过来 ,或者至少完成一套完备的方案 if (!mapData.GetPlayerDataByUnitId(unitData.Id, out var playerData)) return false; if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType, unitData.GiantType,unitData.UnitLevel, out var unitInfo)) return false; //如果上面有人 if (mapData.GetUnitDataByGid(gridData.Id, out var tmp)) return false; //如果是水域 if (gridData.Terrain != TerrainType.Land) { //如果本身就是水生单位 if (unitData.GetLandType() == LandType.WaterOnly || unitData.GetLandType() == LandType.WaterAndAshore || unitData.GetLandType() == LandType.LandAndWater) { if(gridData.Terrain == TerrainType.ShallowSea) return unitData.GetSkill(SkillType.WATERMOVE,out var _); if(gridData.Terrain == TerrainType.DeepSea) return unitData.GetSkill(SkillType.OCEANMOVE,out var _); } //否则 if (gridData.Resource == ResourceType.Bridge) { return true; } //如果有港口但不能下港口 if (gridData.Resource == ResourceType.Port) { if (unitInfo.LandType != LandType.WaterAndAshore && unitInfo.LandType == LandType.WaterOnly) return false; } //如果没有港口,纯看能不能移动进入这片区域 if (gridData.Resource != ResourceType.Port) { //TODO 最好将LandType直接转为Skill ,后续要思考做法 //如果单位不支持进水,直接return false if (!(unitInfo.LandType is LandType.WaterOnly or LandType.LandAndWater)) return false; if(gridData.Terrain == TerrainType.ShallowSea) return unitData.GetSkill(SkillType.WATERMOVE,out var _); if(gridData.Terrain == TerrainType.DeepSea) return unitData.GetSkill(SkillType.OCEANMOVE,out var _); } } //如果是陆地 if (gridData.Terrain == TerrainType.Land) { //如果是山脉 if (gridData.Feature == TerrainFeature.Mountain) { return unitData.GetSkill(SkillType.MOUNTAINMOVE,out var _); } } return true; } } }