2025-07-11 19:06:30 +08:00

876 lines
41 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @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 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(moveType != MoveType.PassiveMove && moveType != MoveType.AttackMove )
unitData.MP = unitData.CP = unitData.AP = 0;
//AUDIO 播放移动音效
AudioManager.Instance.PlayAudio("SFX/UNIT_move");
//如果 land to port
if (unitData.GetLandType() == LandType.LandAndPort && gridData.Resource == ResourceType.Port)
{
LandToBoat(mapData, unitData);
unitData.OnMove(mapData, gridData,moveType);
}
//如果sea to land
else if (unitData.GetLandType() == LandType.WaterAndAshore && gridData.Terrain == TerrainType.Land)
{
BoatToLand(mapData, unitData);
unitData.OnMove(mapData, gridData,moveType);
}
//如果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;
}
// 更新视野情况
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)
{
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;
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 attackendermark和相关参数
unit1.AttackRenderMark = true;
unit1.AttackRenderMarkAttackAnimType = unit1.GetAttackRange() switch
{
1 => AttackAnimType.Melee, 2 => AttackAnimType.Arrow, 3 => AttackAnimType.Bomb,
_ => AttackAnimType.None
};
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 && grid2.Terrain == TerrainType.Land
&& (grid2.Feature != TerrainFeature.Mountain || player1.TechTree.CheckIfHasTech(TechType.Climbing)))
{
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;
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;
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;
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 (!target.CanBeDamaged(mapData, dmg)) return null;
var settlement = new SettlementInfo();
settlement.DamageType = type;
settlement.DamageOrigin = origin;
settlement.DamageTarget = target;
settlement.DamageValue = dmg;
target.Health -= dmg;
if (type == DamageType.KillSelf)
{
UnitDie(mapData, target, dmg);
settlement.IsKill = true;
}
else if (target.CanBeKilled(mapData) && target.Health <= 0)
{
UnitDie(mapData, target, dmg);
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 target.Skills) skill.OnDamaged(mapData, settlement);
foreach (var skill in origin.Skills) skill.OnDamageOther(mapData, settlement);
mapData.OnUnitDamaged(settlement);
return settlement;
}
public void UnitDie(MapData map, UnitData unit, int dmg)
{
unit.Health = 0;
Main.UnitLogic.Die(map, unit);
if (!map.GetGridDataByUnitId(unit.Id, out var targetGrid)) return;
DieAnimRenderMarkUpadate(targetGrid, unit, dmg,true);
}
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角度判断我方单位是否可以移动到某个格子上 不考虑视野信息
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<uint> q = new Queue<uint>();
//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;
//如果是陆地单位,看是否需要减去道路或者桥梁的行动力加成。道路是非敌人领地才行
if (unitData.GetLandType() == LandType.LandAndPort &&
Main.PlayerLogic.HasRoadForUnit(mapData, gridDataX, gridDataY, unitData))
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))
{
//遍历所有我方城市 如果位置没有人且联通,就可以移动过去
var cityDataList = new List<CityData>();
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,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,
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,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.UnitType = UnitType.Boat;
unitData.GiantType = GiantType.None;
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));
unitData.UnitType = unitData.CarryUnitType;
unitData.GiantType = unitData.CarryGiantType;
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(UnitData unitData,UnitType targetType)
{
unitData.UnitType = targetType;
}
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)
{
if (!mapData.GetPlayerDataByUnitId(unitData.Id, out var playerData))
return false;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType, unitData.GiantType,
out var unitInfo))
return false;
//如果上面有人
if (mapData.GetUnitDataByGid(gridData.Id, out var tmp))
return false;
//如果是水域
if (gridData.Terrain != TerrainType.Land)
{
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)
{
if(gridData.Terrain == TerrainType.ShallowSea)
return unitData.Skills.Contains(SkillFactory.GetSkillBySkillType(SkillType.WATERMOVE));
if(gridData.Terrain == TerrainType.DeepSea)
return unitData.Skills.Contains(SkillFactory.GetSkillBySkillType(SkillType.OCEANMOVE));
}
}
//如果是陆地
if (gridData.Terrain == TerrainType.Land)
{
//如果是山脉
if (gridData.Feature == TerrainFeature.Mountain)
{
return unitData.Skills.Contains(SkillFactory.GetSkillBySkillType(SkillType.MOUNTAINMOVE));
}
}
return true;
}
}
}