2026-02-27 14:22:21 +08:00

1551 lines
73 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 System.Numerics;
using Animancer;
using Logic.Action;
using Logic.Audio;
using Logic.CrashSight;
using Logic.Skill;
using ParadoxNotion;
using UnityEngine;
using RuntimeData;
using TH1_Anim;
using TH1_Anim.Fragments;
using TH1_Core.Events;
using TH1_Core.Managers;
using TH1_Logic.Core;
using TH1_Presentation.Sequencer.Task;
using TH1Renderer;
using Unity.VisualScripting;
using Vector2 = UnityEngine.Vector2;
namespace Logic
{
public enum DamageType
{
ActiveAttack,
CounterAttack,
FollowAttack,
Splash,
True,
KillSelf,
}
public enum HealType
{
AttackAllyHeal,
SelfHeal
}
public class SettlementInfo
{
//伤害类型
public DamageType DamageType;
//伤害来源的unitId
public UnitData DamageOrigin;
//伤害目标的unitId
public UnitData DamageTarget;
public GridData DamageTargetGrid;
public CityData DamageTargetCity;
//伤害值
public int DamageValue;
//是否击杀
public bool IsKill;
public bool IsFinished;
// 血量减少值
public int HealthReduceValue;
public SettlementInfo()
{
IsFinished = false;
}
}
public class UnitLogic : IUnitLogic
{
float[,] MovementCostMap;
float[,] MovementRemainMap;
float[,] MoveRealCostMap;//最优路线下从别处移动过来的实机cost花费多少
//ReachMap组常规移动能抵达的map
bool[,] NormalReachMap;//常规计算
bool[,] MomijiHunterReachMap; //专门给momijiHunter 用的 判断一个格子如果不是hunter周围还是否有行动力抵达。如果一个角色没有momijihunter技能这个就没用
bool[,] FinalReachMap;//最终整合reachMap将常规计算和momijiHunter整合在一起判定在常规移动下哪些格子可以抵达哪些不可以抵达
bool[,] TransMap;//在常规移动之外,额外赋予的特殊可传送格子
bool[,] SanaeMap;//在常规移动之外,额外赋予的特殊可抵达格子
public UnitLogic()
{
MovementCostMap = new float[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
MovementRemainMap = new float[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
MoveRealCostMap = new float[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
NormalReachMap = new bool[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
MomijiHunterReachMap = new bool[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
FinalReachMap = new bool[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
TransMap = new bool[Table.Instance.MaxMapSize,Table.Instance.MaxMapSize];
SanaeMap = new bool[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 void MoveTo(MapData mapData, PlayerData playerData, UnitData unitData, GridData gridData)
{
// 攻击行为切换成 Action 驱动
var moveId = new CommonActionId { ActionType = CommonActionType.UnitMove };
var moveAction = new UnitMoveAction(moveId);
var param = new CommonActionParams(mapData, playerData:playerData, unitData:unitData, gridData:gridData);
param.RefreshParams();
moveAction.CompleteExecute(param);
}
//-------- 执行类 --------//
public void MoveToLogic(MapData mapData, UnitData unitData, GridData gridData, MoveType moveType,
List<Vector2Int> path = null)
{
//处理移动目标有单位的情况(return false)
var targetGridUnit = gridData.Unit(mapData);
if (targetGridUnit != null) return;
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;
//如果 land to port
if (unitData.GetLandType() == LandType.LandAndPort && gridData.Resource == ResourceType.Port)
{
//注意必须先Onmove变身之前的技能用掉再变身)
unitData.OnMove(mapData, gridData, moveType, path);
LandToBoat(mapData, unitData);
}
//如果sea to land
else if (unitData.GetLandType() == LandType.WaterAndAshore && gridData.Terrain == TerrainType.Land)
{
//注意必须先Onmove变身之前的技能用掉再变身)
unitData.OnMove(mapData, gridData, moveType, path);
BoatToLand(mapData, unitData);
}
//如果sea to sea或者land to land
else
//处理DASH等等技能情况在Move结束时会触发的那些技能
unitData.OnMove(mapData, gridData, moveType, path);
mapData.OnAnyUnitMove(mapData, unitData, gridData, moveType);
if (!mapData.GetPlayerDataByUnitId(unitData.Id, out var playerData))
{
LogSystem.LogError("unit找不到player");
return;
}
//如果是盟友的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;
}
//更新所有人的外交好感情况。临时用的。必须真map才做AI预测的不做性能爆炸了
if (mapData == Main.MapData)
{
foreach (var pData in mapData.PlayerMap.PlayerDataList)
pData.RefreshFeelingValue(mapData);
}
//判断unit是否走到了另一个玩家的城市里如果是要设置两国状态为战争
if (mapData.GetCityDataByGid(gridData.Id, out var city)
&& mapData.GetPlayerDataByCityId(city.Id, out var player2)
&& mapData.GetPlayerDataByUnitId(unitData.Id, out var player1)
&& player1.Id != player2.Id)
{
player2.AddAttacker(player1.Id);
Main.PlayerLogic.SetDiplomacyLeague(mapData, player1, player2, DiplomacyState.War);
}
}
//返回unit2是否能反击unit1
public bool CanCounter(MapData mapData, UnitData unit1, UnitData unit2)
{
if (!unit2.CanAttackAll(mapData) && mapData.IsLeagueUnitByUnit(unit1.Id, unit2.Id)) return false;
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(mapData))
canCounter = false;
if (dmg1 >= unit2.Health) //如果伤害直接够杀死对方
canCounter = false;
return canCounter;
}
public void Attack(MapData mapData, UnitData unit1, UnitData unit2, out int attackDmg,out int counterDmg,out FragmentType fragmentType)
{
attackDmg = 0;
counterDmg = 0;
fragmentType = FragmentType.Attack;
if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return;
if (!mapData.GetPlayerDataByUnitId(unit2.Id, out var player2)) return;
if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return;
if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return;
if (unit1.IsLimitSelfAttack(mapData)) return;
// 攻击前生命周期
unit1.BeforeActiveAttackOther(mapData, unit1, unit2, out var tmpAddDmg);
attackDmg += tmpAddDmg;
var attackDistance = Table.Instance.CalcDistance(grid1, grid2);
// 计算攻击伤害
int dmg1 = Table.Instance.CalcDamage(mapData, unit1, unit2);
int dmg2 = Table.Instance.CalcCounterDamage(mapData, unit1,unit2 );
//判断对方能否反击的参数
//TODO 之后还是要把这些放到后面逻辑里去写,不要在最开头判断,一定判断不出来的
bool canCounter = CanCounter(mapData,unit1,unit2);
if(unit1.UnitFullType.UnitType == UnitType.Minder)canCounter = false;
attackDmg += dmg1;
//攻击会消耗所有类别的行动点数
unit1.AP = unit1.CP = unit1.MP = 0;
Main.UnitLogic.DamageSettlement(mapData, unit1, unit2, dmg1, DamageType.ActiveAttack);
if (!unit2.IsAlive())
{
fragmentType = FragmentType.NotMoveKill;
//处理MoveKill的情况
if (unit1.IsCanMoveKill(mapData) && attackDistance <= 1 && CheckUnitAbleForGrid_OfflineStatus(mapData, player1, unit1, grid2) && grid2.Unit(mapData) == null)
{
Main.UnitLogic. MoveToLogic(mapData, unit1, grid2, MoveType.AttackMove);
//如果杀了人且可以移动过去动画类型就是Movekill
fragmentType = FragmentType.MoveKill;
}
else
{
//如果杀了人且可以移动过去动画类型就是之前已经设置好的默认的Attack
}
}
else
{
if (canCounter && unit1.IsAlive() && unit2.IsAlive())
{
counterDmg += dmg2;
Main.UnitLogic.DamageSettlement(mapData, unit2, unit1, dmg2, DamageType.CounterAttack);
if(!unit1.IsAlive()) fragmentType = FragmentType.AttackAndCounterDie;
else fragmentType = FragmentType.AttackAndCounter;
}
}
if (unit1.IsAlive()) unit1.AfterActiveAttackOther(mapData, unit1, unit2);
if (unit2.IsAlive()) unit2.AfterActiveAttacked(mapData, unit1, unit2);
}
// 伤害新结算方法所有的掉血都要走此方法不允许直接修改UnitData.Health
// TODO 自杀或者杀队友要不要加 player.TotalKill
public SettlementInfo DamageSettlement(MapData mapData, UnitData origin, UnitData target, int dmg, DamageType type)
{
if (origin == null || target == null)
{
LogSystem.LogError($"DamageSettlement !!! DamageSettlement origin:{origin} or target{target} is null");
return null;
}
if (!mapData.GetGridDataByUnitId(target.Id, out var targetGrid))
{
LogSystem.LogError($"DamageSettlement !!! Target Grid is null target.id:{target.Id}");
return null;
}
if (!mapData.GetCityDataByUnitId(target.Id, out var targetCity))
{
LogSystem.LogError($"DamageSettlement !!! Target City is null target.id:{target.Id}");
return null;
}
//TODO 和白哉check下是不是这么改
if (!origin.CanAttackAll(mapData) && mapData.IsLeagueUnitByUnit(origin.Id, target.Id) && type != DamageType.KillSelf)
{
LogSystem.LogError($"DamageSettlement !!! mapData.IsLeagueUnitByUnit(origin.Id, target.Id) && type != DamageType.KillSelf)");
return null;
}
if (!mapData.GetPlayerDataByUnitId(origin.Id, out var player1)) return null;
if (!mapData.GetPlayerDataByUnitId(target.Id, out var player2)) return null;
if (!target.CanBeDamaged(mapData, dmg)) {
LogSystem.LogError($"DamageSettlement !!! 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;
settlement.DamageTargetCity = targetCity;
origin.BeforeDamageOther(mapData, settlement);
target.BeforeDamagedSupportStage(mapData, settlement);
target.BeforeDamagedTransformStage(mapData, settlement);
mapData.BeforeUnitDamaged(settlement);
if (settlement.IsFinished) return null;
settlement.HealthReduceValue = Mathf.Min(target.Health, settlement.DamageValue);
target.Health -= settlement.DamageValue;
if (type == DamageType.KillSelf && !target.IsAlive())
{
UnitDie(mapData, target, settlement.DamageValue);
settlement.IsKill = true;
}
else if (target.CanBeKilled(mapData) && !target.IsAlive())
{
if (Main.MapData == mapData) AchievementDataManager.Instance.OnKillUnit(mapData, origin, target);
UnitDie(mapData, target, settlement.DamageValue);
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;
}
// unit 受伤和伤害他人时回调
origin.OnDamageOther(mapData, settlement);
target.OnDamaged(mapData, settlement);
mapData.OnUnitDamaged(settlement);
// 任务三层调用
player1.PlayerHeroData.HeroTaskDict.TryGetValue(origin.UnitFullType, out var task1);
task1?.OnDamageOther(mapData, settlement);
player1.PlayerHeroData.HeroTaskDict.TryGetValue(origin.OriginUnitFullType, out var task1_origin);
task1_origin?.OnDamageOther(mapData, settlement);
player1.PlayerHeroData.HeroTaskDict.TryGetValue(origin.CarryUnitFullType, out var task1_carry);
task1_carry?.OnDamageOther(mapData, settlement);
foreach (var kv in player1.PlayerHeroData.HeroTaskDict) kv.Value.OnPlayerDamageOther(mapData, settlement);
foreach (var player in mapData.PlayerMap.PlayerDataList)
{
foreach (var kv in player.PlayerHeroData.HeroTaskDict) kv.Value.OnAnyDamageOther(mapData, settlement);
}
player2.PlayerHeroData.HeroTaskDict.TryGetValue(target.UnitFullType, out var task2);
task2?.OnDamaged(mapData, settlement);
foreach (var kv in player2.PlayerHeroData.HeroTaskDict) kv.Value.OnPlayerDamaged(mapData, settlement);
foreach (var player in mapData.PlayerMap.PlayerDataList)
{
foreach (var kv in player.PlayerHeroData.HeroTaskDict) kv.Value.OnAnyDamaged(mapData, settlement);
}
// unit 受伤和伤害他人后回调
origin.AfterDamageOther(mapData, settlement);
if (type is DamageType.ActiveAttack or DamageType.FollowAttack)
{
Main.PlayerLogic.SetDiplomacyLeague(mapData,player1, player2, DiplomacyState.War);
player1.CurAttackPlayers.Add(player2.Id);
player2.CurAttackPlayers.Add(player1.Id);
player2.AddAttacker(player1.Id);
player1.TurnNoAttack = 0;
//更新所有人的外交好感情况。临时用的
foreach(var pData in mapData.PlayerMap.PlayerDataList)
pData.RefreshFeelingValue(mapData);
}
//Step #4 Moment Process
if (mapData == Main.MapData && settlement.IsKill)
{
//注意这里只判断我方击杀的moment,我方被杀的moment要在unitDie里判断
bool killHero = false;
bool killTreasure = false;
bool killFull = false;
bool killBigGuy = false;
bool killWithLow = false;
bool killOfficer = false;
bool oneHP = false;
bool counterLow = false;
if (origin.Player(mapData) == mapData.PlayerMap.SelfPlayerData)
{
if(target.IsHero(true))killHero = true;
if (settlement.DamageValue >= target.GetMaxHealth()) killFull = true;
if(target.IsBigGuy(true))killBigGuy = true;
if(target.IsOfficer())killOfficer = true;
if (target.Grid(mapData, out var grid) && grid.Resource == ResourceType.Treasure) killTreasure = true;
if (origin.Health < 5 && origin.Health < origin.GetMaxHealth() * 0.3f) killWithLow = true;
}
if (target.Player(mapData) == mapData.PlayerMap.SelfPlayerData)
{
if (target.Health == 1) oneHP = true;
}
var momentsubType = MomentSubType.None;
if (killHero) momentsubType = MomentSubType.BattleKillHero;
else if (killTreasure)momentsubType = MomentSubType.BattleKillTreasure;
else if (killFull)momentsubType = MomentSubType.BattleKillFullHealth;
else if (killBigGuy)momentsubType = MomentSubType.BattleKillGiant;
else if (killWithLow)momentsubType = MomentSubType.BattleNarrowVictory;
else if (killOfficer)momentsubType = MomentSubType.BattleKillOfficer;
else if (oneHP)momentsubType = MomentSubType.BattleLastStand;
else if (counterLow)momentsubType = MomentSubType.BattleCounterKill;
if(momentsubType != MomentSubType.None)
EventManager.Publish(new ShowUINotifyMoment()
{
MomentSubType = momentsubType,
Empire = Main.MapData.PlayerMap.SelfPlayerData.Empire
});
}
return settlement;
}
//TODO 这里RecoverHealth即可处理了视觉要改为视觉和逻辑分离
//所有恢复都走这个函数
public int RecoverHealth(MapData map, UnitData origin, UnitData target, int recover)
{
//Step #0 鲁棒性处理,设置基本参数
if (origin == null || target == null)
{
LogSystem.LogError($"DamageSettlement origin:{origin} or target{target} is null");
return 0;
}
var originPlayer = target.Player(map);
if (originPlayer == null)
{
LogSystem.LogError($"Origin Player is null target.id:{origin.Id}");
return 0;
}
//Step #1处理恢复生命
int realRecover = target.AddHealth(recover);
//Step #2处理恢复技能的herotask生命周期
origin.HeroTask(map)?.OnHealthReturn(map,realRecover,recover);
//Step #3 处理View
var grid = target.Grid(map);
if (grid != null)
grid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Heal));
target.Renderer(map)?.InstantUpdateUnit(true);
origin.Renderer(map)?.InstantUpdateUnit(true);
return realRecover;
}
//自然死亡
public void UnitDie(MapData map, UnitData unit, int dmg)
{
unit.Health = 0;
if (!map.GetGridDataByUnitId(unit.Id, out var targetGrid)) return;
bool notifymoment = false;
//如果是伟人死亡对应的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;
//如果是英雄战船死亡对应的player受到惩罚 penalty=4
if (unit.UnitType == UnitType.GiantJuggernaut && unit.CarryUnitType == UnitType.Giant
&& map.GetPlayerDataByUnitId(unit.Id, out var player2) &&
Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(
unit.CarryUnitType, unit.CarryGiantType,
unit.CarryUnitLevel,
out var info2))
player2.giantPenalty[(uint)info2.ChessType] = 4;
if (map == Main.MapData && unit.Player(map) == map.PlayerMap.SelfPlayerData)
{
if (unit.UnitType == UnitType.Giant || unit.CarryUnitType == UnitType.Giant)
{
EventManager.Publish(new ShowUINotifyMoment()
{
MomentSubType = MomentSubType.BattleHeroDie,
Empire = unit.Player(map).Empire,
});
}
if(unit.IsBigGuy(true))
{
EventManager.Publish(new ShowUINotifyMoment()
{
MomentSubType = MomentSubType.BattleSacrify,
Empire = unit.Player(map).Empire,
});
}
if (unit.Grid(map, out var grid) && grid.Resource == ResourceType.Treasure)
{
EventManager.Publish(new ShowUINotifyMoment()
{
MomentSubType = MomentSubType.BattleTreasure,
Empire = unit.Player(map).Empire,
});
}
}
if (notifymoment && map == Main.MapData && unit.Player(map) == map.PlayerMap.SelfPlayerData)
{
EventManager.Publish(new ShowUINotifyMoment()
{
MomentSubType = MomentSubType.BattleHeroDie,
Empire = unit.Player(map).Empire,
});
}
map.SetUnitDataDie(unit);
map.OnAnyUnitDie(map);
}
//unit非自然死亡如背盟、国家灭亡、解散、被挤死等
public void UnitUnnaturalDie(MapData map, UnitData unit)
{
unit.Health = 0;
//如果是伟人死亡对应的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;
map.SetUnitDataDie(unit);
}
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 (MovementRemainMap[i, j] < 0)
t += "@";
else t += ((int)MovementRemainMap[i, j]).ToString();
t += "\n";
}
Debug.Log(t);
}
//计算单位移动或者攻击的网格数组存储在InfoMap中方便后续判断使用是一个需要立刻使用的非长久存储的临时infomap
public void CalcUnitMoveInfo(MapData mapData, uint uid)
{
//Step #1 设置好各种初始参数
mapData.UnitMap.GetUnitDataByUnitId(uid, out var unitData);
mapData.GetPlayerDataByUnitId(uid, out var playerData);
mapData.GetGridDataByUnitId(uid, out var gridData);
if (unitData == null || playerData == null || gridData == null)
{
LogSystem.LogError($"CalcUnitMoveInfo Error !!! unitData:{unitData} playerData:{playerData} gridData:{gridData}");
return;
}
int width = (int)mapData.MapConfig.Width;
int height = (int)mapData.MapConfig.Height;
//Step #2 根据地形先设置好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() is LandType.LandAndPort)
{
MovementCostMap[i, j] = 1;
if (targetGridData.Vegetation == Vegetation.Trees)
{
MovementCostMap[i, j] = 3;
if (unitData.GetSkill(SkillType.BAMBOOMOVE, out var _))
MovementCostMap[i, j] = 1;
}
if (targetGridData.Feature == TerrainFeature.Mountain)
MovementCostMap[i, j] = unitData.GetSkill(SkillType.MOUNTAINMOVE, out var _) ? 3 : -1;
if (targetGridData.Terrain == TerrainType.ShallowSea)
{
if (targetGridData.Resource == ResourceType.Bridge)
MovementCostMap[i, j] = 1;
//如果是port要判断是盟军的port 还是其他的port
else if (targetGridData.Resource == ResourceType.Port)
{
//如果没有port科技不能去
if (!unitData.GetSkill(SkillType.WATERMOVE, out var _))
MovementCostMap[i, j] = -1;
//如果是己方的port
else if (mapData.GetPlayerDataByTerritoryGridId(targetGridData.Id, out var targetGridPlayer)
&& mapData.SameUnion(targetGridPlayer.Id, playerData.Id))
MovementCostMap[i, j] = 999;
else
MovementCostMap[i, j] = -1;
}
else MovementCostMap[i, j] = -1;
}
if (targetGridData.Terrain == TerrainType.DeepSea)
MovementCostMap[i, j] = -1;
}
else if (unitData.GetLandType() is LandType.LandAndWater)
{
MovementCostMap[i, j] = 1;
if (targetGridData.Vegetation == Vegetation.Trees)
{
MovementCostMap[i, j] = 3;
if (unitData.GetSkill(SkillType.BAMBOOMOVE, out var _))
MovementCostMap[i, j] = 1;
}
if (targetGridData.Feature == TerrainFeature.Mountain){
MovementCostMap[i, j] = unitData.GetSkill(SkillType.MOUNTAINMOVE, out var _) ? 3 : -1;
}
if (targetGridData.Terrain == TerrainType.ShallowSea)
{
MovementCostMap[i, j] = 1;
//如果没有WATERMOVE
if (!unitData.GetSkill(SkillType.WATERMOVE, out var _))
MovementCostMap[i, j] = -1;
}
if (targetGridData.Terrain == TerrainType.DeepSea)
{
MovementCostMap[i, j] = 1;
if (!unitData.GetSkill(SkillType.OCEANMOVE, out var _))
MovementCostMap[i, j] = -1;
}
}
else if (unitData.GetLandType() is LandType.LandOnly)
{
MovementCostMap[i, j] = 1;
if (targetGridData.Vegetation == Vegetation.Trees)
{
MovementCostMap[i, j] = 3;
if (unitData.GetSkill(SkillType.BAMBOOMOVE, out var _))
MovementCostMap[i, j] = 1;
}
if (targetGridData.Feature == TerrainFeature.Mountain)
MovementCostMap[i, j] = unitData.GetSkill(SkillType.MOUNTAINMOVE, out var _) ? 3 : -1;
if (targetGridData.Terrain is TerrainType.ShallowSea or TerrainType.DeepSea)
{
if (targetGridData.Resource == ResourceType.Bridge)
MovementCostMap[i, j] = 1;
else
MovementCostMap[i, j] = -1;
}
}
//如果是水上单位
else if (unitData.GetLandType() is LandType.WaterAndAshore or LandType.WaterOnly)
{
if (targetGridData.Terrain == TerrainType.DeepSea)
MovementCostMap[i, j] = unitData.GetSkill(SkillType.OCEANMOVE, out var _) ? 1 : -1;
else if (targetGridData.Terrain == TerrainType.ShallowSea)
MovementCostMap[i, j] = 1;
else
{
MovementCostMap[i, j] = 999;
if (targetGridData.Feature == TerrainFeature.Mountain &&
!unitData.GetSkill(SkillType.MOUNTAINMOVE, out var _))
MovementCostMap[i, j] = -1;
}
}
else if (unitData.GetLandType() is LandType.Fly)
{
MovementCostMap[i, j] = 1;
}
}
//没有视野,那就不可抵达
else
MovementCostMap[i, j] = -1;
//特殊处理MORIYAKNIGHTMOVE
if (unitData.GetSkill(SkillType.MORIYAKNIGHTMOVE, out var _))
{
//如果是山脉且没有城市在上面
if (targetGridData.Terrain == TerrainType.Land &&
!(targetGridData.Feature == TerrainFeature.Mountain || targetGridData.CityOnGrid(mapData, out var _)))
{
//不是山或者城市中心的陆地,不可达
MovementCostMap[i, j] = -1;
}
}
if (MovementCostMap[i, j] == 0)
{
Debug.Log("Fatal Error !!!!");
}
}
//Step #3 将所有敌人的控制区的costMap设置为999
if(!unitData.GetSkill(SkillType.SNEAK,out var _))
foreach (UnitData B in mapData.UnitMap.UnitList)
{
if (!B.IsAlive()) continue;
mapData.GetPlayerDataByUnitId(B.Id, out var playerDataB);
mapData.GetGridDataByUnitId(B.Id, out var gridDataB);
//盟友则不会有控制区域
if (mapData.SameUnion(playerDataB.Id ,playerData.Id)) continue;
var targetGridDataList = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataB);
foreach (var targetGridData in targetGridDataList)
{
int x = targetGridData.Pos.X, y = targetGridData.Pos.Y;
MovementCostMap[x, y] = (MovementCostMap[x, y] < 0) ? -1 : 999;
}
MovementCostMap[gridDataB.Pos.X,gridDataB.Pos.Y] = 999;
}
//Step #4将所有友军的城市中心costmap设置为-1(不能进入友军的城市中心)
foreach (var tcity in mapData.CityMap.CityList)
{
if (!mapData.GetPlayerDataByCityId(tcity.Id, out var tplayer))continue;
if (tplayer.Id == playerData.Id) continue;
if(!mapData.SameUnion(tplayer.Id,playerData.Id))continue;
if(!mapData.GetGridDataByCityId(tcity.Id,out var tgrid))continue;
MovementCostMap[tgrid.Pos.X,tgrid.Pos.Y] = -1;
}
//Step #5 初始化remainmap表示剩余多少movement< 0 则不再扩展。ReachMap则用来确定是否可达
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
MovementRemainMap[i, j] = -999;
MoveRealCostMap[i, j] = 999;
NormalReachMap[i, j] = false;
TransMap[i, j] = false;
SanaeMap[i, j] = false;
MomijiHunterReachMap[i, j] = false;
}
//设置初始行动力
//注意这里使用的是FinalMoveRange和MoveRange不一样MoveRange是行动力Fixed则是在这之上还有一层修改目前主要用于Kanako
MovementRemainMap[gridData.Pos.X,gridData.Pos.Y] = unitData.FinalMoveRange(mapData);
NormalReachMap[gridData.Pos.X, gridData.Pos.Y] = true;
MoveRealCostMap[gridData.Pos.X, gridData.Pos.Y] = 0;
//Step #6然后进行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 (MovementRemainMap[gridDataX.Pos.X, gridDataX.Pos.Y] < 0.1f) continue;
var nearby = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataX);
foreach (var gridDataY in nearby)
{
float cost = MovementCostMap[gridDataY.Pos.X, gridDataY.Pos.Y];
//如果不可达区
if (cost <= 0)continue;
//如果是陆地单位看是否需要减去道路或者桥梁的行动力加成。道路是非敌人领地才行并且目标不是耗光所有cost的港口或者敌占区
if ((unitData.GetLandType() is LandType.LandAndPort or LandType.LandOnly or LandType.LandAndWater) &&
Main.PlayerLogic.HasRoadForUnit(mapData, gridDataX, gridDataY, unitData) && cost < 900)
cost = 0.5f;
//如果能够发生更新
if (MovementRemainMap[gridDataX.Pos.X, gridDataX.Pos.Y] - cost > MovementRemainMap[gridDataY.Pos.X, gridDataY.Pos.Y])
{
MovementRemainMap[gridDataY.Pos.X, gridDataY.Pos.Y] = MovementRemainMap[gridDataX.Pos.X, gridDataX.Pos.Y] - cost;
NormalReachMap[gridDataY.Pos.X, gridDataY.Pos.Y] = true;
MoveRealCostMap[gridDataY.Pos.X, gridDataY.Pos.Y] = cost;
//如果xy不再队列里并且还剩余有行动力就加入队列
if (!q.Contains(gridDataY.Id) && MovementRemainMap[gridDataY.Pos.X, gridDataY.Pos.Y] > 0.1f)
q.Enqueue(gridDataY.Id);
}
}
}
//Step #7 -------------------------------------------先处理常规移动的最终情况-------------------------------
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
FinalReachMap[i,j] = NormalReachMap[i, j];
// MOMIJIHUNTER 技能拓展做法是让所有MOVEInfoMap -= MoveFloor
MOMIJIHUNTER_SkillHandler(mapData,unitData);
//Step #8 ----------------------------------------------------这里开始处理各种特殊情况--------------------------------------
//处理SAKUYAFLY技能可以移动到己方伟人的身周
if (unitData.IsCanMoveGiantNearbyGrid(mapData))
{
//遍历所有单位,找到己方的伟人或者小恶魔
foreach (var unitA in mapData.UnitMap.UnitList)
if((unitA.TreatedAsHero(mapData,unitData))
&& 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_NOCENTER(1,1,gridB);
foreach (var gridC in nearby){
int xx = gridC.Pos.X, yy = gridC.Pos.Y;
if(CheckUnitAbleForGrid_OfflineStatus(mapData,playerData,unitData,gridC)
&& !mapData.GetUnitDataByGid(gridC.Id,out var tt))
{
int x = gridC.Pos.X, y = gridC.Pos.Y;
TransMap[x, y] = true;
}}
}
}
//接入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<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;
TransMap[gridB.Pos.X, gridB.Pos.Y] = true;
}
}
}
//接入MORIYAKNIGHTMOVE,在城市中心和天狗酒馆之间传送
if (unitData.GetSkill(SkillType.MORIYAKNIGHTMOVE,out var _) && unitData.Grid(mapData,out var curGrid) && (curGrid.Resource is ResourceType.MoriyaMilitary or ResourceType.CityCenter))
{
//遍历所有我方城市和军营 如果位置没有人就可以移动过去
var cityDataList = new List<CityData>();
mapData.GetCityDataListByPlayerId(playerData.Id, cityDataList);
foreach (var targetCity in cityDataList)
{
if (!targetCity.Grid(mapData, out var targetGrid)) continue;
if (targetGrid.Id == curGrid.Id) continue;
if (targetGrid.Unit(mapData, out var _)) continue;
TransMap[targetGrid.Pos.X, targetGrid.Pos.Y] = true;
}
foreach (var targetCity in cityDataList)
{
//然后处理该城市的军营
var gidSet = targetCity.Territory.TerritoryArea;
foreach (var targetGid in gidSet)
{
if (targetGid == curGrid.Id) continue;
if (!mapData.GridMap.GetGridDataByGid(targetGid, out var targetGridB))continue;
if (targetGridB.Resource != ResourceType.MoriyaMilitary) continue;
if (targetGridB.Unit(mapData,out var _)) continue;
TransMap[targetGridB.Pos.X, targetGridB.Pos.Y] = true;
}
}
}
// SANAEWIND{X} 技能拓展
if (unitData.GetSkill(SkillType.SANAEWINDX, out var skill))
{
var sanaeWindXSkill = skill as SanaeWindXSkill;
if (sanaeWindXSkill != null)
{
var targetVec2 = new List<Vector2Int>();
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (!NormalReachMap[i, j]) continue;
targetVec2.Add(new Vector2Int(i, j));
}
}
foreach (var vec in targetVec2)
{
var newVec = vec + sanaeWindXSkill.GetOffset();
if (newVec.x < 0 || newVec.x >= width || newVec.y < 0 || newVec.y >= height) continue;
if (!mapData.GridMap.GetGridDataByPos(vec.x, vec.y, out var oldGrid)) continue;
if (!mapData.GridMap.GetGridDataByPos(newVec.x, newVec.y, out var tmpGrid)) continue;
if (!CheckUnitAbleForGrid_RealTimeStatus(mapData, unitData, tmpGrid)) continue;
//如果是陆地单位,然后经过了港口,那么不能再飞起来
if (oldGrid.Resource == ResourceType.Port &&
unitData.GetLandType() is LandType.LandAndPort or LandType.LandOnly)
continue;
//不能进入友方城市中心
if (tmpGrid.CityOnGrid(mapData, out var city))
{
if (!city.Player(mapData, out var playerA)) continue;
if (!unitData.Player(mapData, out var playerB)) continue;
if (playerA.Id != playerB.Id && mapData.SameUnionOrJustBreakUnion(playerA.Id, playerB.Id)) continue;
}
SanaeMap[newVec.x, newVec.y] = true;
}
}
}
// MORIYAKNIGHTMOVE 技能拓展(主要是消除早苗的影响)
if (unitData.GetSkill(SkillType.MORIYAKNIGHTMOVE, out var _))
{
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
if (mapData.GridMap.GetGridDataByV2(new Vector2(i, j), out var grid) &&
grid.Terrain == TerrainType.Land &&
!(grid.Feature == TerrainFeature.Mountain || grid.CityOnGrid(mapData,out var _)
))
{
SanaeMap[i,j] = false;
}
}
}
private bool MOMIJIHUNTER_SkillHandler(MapData mapData,UnitData unitData)
{
int width = (int)mapData.MapConfig.Width;
int height = (int)mapData.MapConfig.Height;
if (!unitData.GetSkill(SkillType.MOMIJIHUNTER, out var skillHunter)) return false;
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
if (!NormalReachMap[i, j]) continue;
//floor 算出亏欠几点移动力
var floor = unitData.GetGridMoveFloor(mapData, unitData,
mapData.GridMap.GetGridDataByV2(new Vector2(i,j),out var tmpGrid) ? tmpGrid : null);
//如果剩余行动力 减去亏欠的移动力再加上上一次移动过来的realcost仍然>=0那么说明这个格子是仍然可以抵达的
MomijiHunterReachMap[i, j] = (MovementRemainMap[i, j] + MoveRealCostMap[i, j] - floor) > 0.1f;
}
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
FinalReachMap[i,j] = NormalReachMap[i, j] && MomijiHunterReachMap[i, j];
return true;
}
// 检查单位是否能攻击目标点
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(mapData)) return false;
return true;
}
// None, Move, Attack, Port, MoveAshore, Ally,AttackGround
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);
//如果目标位置上面有友军单位除非有特殊技能否则是none
if (mapData.SameUnionOrJustBreakUnion(playerDataA.Id, playerDataB.Id))
{
if (mapData.GridMap.CalcDistance(gridDataA, gridDataB) <= unitDataA.GetAttackRange(mapData))
{
//处理Flandre技能可以是刚解除结盟的友军
if (unitDataA.GetSkill(SkillType.FLANDREATTACK, out var _) )
return MoveAttackType.Attack;
//以下处理attackAlly友军的情况不能背盟
if (mapData.SameUnion(playerDataA.Id, playerDataB.Id) && unitDataA.IsCanAttackAlly())
{
if(unitDataA.IsCanAttackTargetAlly(mapData,unitDataB))
return MoveAttackType.Ally;
}
}
return MoveAttackType.None;
}
//如果目标位置上面有敌军单位,那不可移动,但是有可能可以攻击
else if (!unitDataA.IsLimitSelfAttack(mapData))
//具备攻击能力
{
if (mapData.GridMap.CalcDistance(gridDataA, gridDataB) <=
unitDataA.GetAttackRange(mapData))
//判断距离在攻击距离范围内,
{
//特殊处理:学者不能攻击英雄
if (unitDataA.GetSkill(SkillType.CONVERT, out var _) &&
unitDataB.UnitFullType.UnitType == UnitType.Giant) return MoveAttackType.None;
/*if(mapData.GridMap.CalcDistance(gridDataA, gridDataB) > 3)
Debug.Log("异常的攻击距离");*/
return MoveAttackType.Attack; //返回可以攻击
}
else if(unitDataA.GetSkill(SkillType.KANAKOBATTLEFIELDPRO,out var _) && unitDataA.GetSkill(SkillType.KANAKOSITTING,out var _))
//不在攻击范围内,有一种可能 kanako
{
var tmpSet = mapData.GridMap.GetAroundGridDataSet_NOCENTER(1,1,gridDataB);
bool can = false;
foreach(var tmpGrid in tmpSet)
if(tmpGrid.Unit(mapData,out var tmpUnit) && tmpUnit.Player(mapData) == gridDataA.Player(mapData)
&& (tmpUnit.GiantType != GiantType.None ||
(tmpUnit.UnitType == UnitType.MoriyaHebi && tmpUnit.UnitLevel >= 3))
&& tmpUnit.Grid(mapData).Feature == TerrainFeature.Mountain)
{
can = true;
break;
}
if (can) return MoveAttackType.Attack;
}
}
return MoveAttackType.None;
}
//如果目标位置没有任何单位
else
{
//如果满足AttackGround优先处理
if (unitDataA.IsCanAttackTargetGrid(mapData, gridDataB))
return MoveAttackType.AttackGround;
//如果不能在非我方领土移动,优先判断
if (unitDataA.IsLimitMoveToSelfTerrain(Main.MapData) &&
unitDataA.Player(Main.MapData) != gridDataB.Player(Main.MapData))
return MoveAttackType.None;
//如果是传送或者sanae最优先判断
if (TransMap[gridDataB.Pos.X, gridDataB.Pos.Y]) return MoveAttackType.MoveTeleport;
if (SanaeMap[gridDataB.Pos.X, gridDataB.Pos.Y]) return MoveAttackType.Move;
//如果常规移动不可达直接return
if (!FinalReachMap[gridDataB.Pos.X, gridDataB.Pos.Y]) 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;
}
//默认在调用这个函数前已经调用了 CalcUnitMoveInfo(A),计算好了。不组合使用是不行的
public MoveAttackType GetMovePath(MapData map, Vector2Int start, Vector2Int end,out List<Vector2Int> path)
{
if (TransMap[end.x, end.y])
{
path = new List<Vector2Int> { start, end };
return MoveAttackType.MoveTeleport;
}
path = new List<Vector2Int>();
Vector2Int[] Dirs =
{
new(-1, 0), new(0, -1),new(0, 1),new(1, 0),
new(1, -1), new(1, 1),new(-1, -1),new(-1, 1)
};
if (start == end)
{
path = new List<Vector2Int> { start };
return MoveAttackType.Move;
}
var queue = new Queue<Vector2Int>();
// 用字典记录路径来源,同时兼作“已访问”集合
var cameFrom = new Dictionary<Vector2Int, Vector2Int>();
uint width = map.MapConfig.Width;
uint height = map.MapConfig.Height;
queue.Enqueue(start);
cameFrom[start] = start; // 标记起点已访问
while (queue.Count > 0)
{
var current = queue.Dequeue();
float currentHeight = MovementRemainMap[current.x, current.y];
if (current == end) break; // 首次到达终点,即为最短路径
//if (currentHeight < 0) continue; 这里不能直接continue因为有sanaeMap存在 即使<0也可以继续移动到sanaemap上
foreach (var dir in Dirs)
{
var next = current + dir;
// 检查1.越界 2.已访问
if (next.x < 0 || next.x >= width || next.y < 0 || next.y >= height || cameFrom.ContainsKey(next))
continue;
// 检查3.高度非增
if (MovementRemainMap[next.x, next.y] < currentHeight || SanaeMap[next.x, next.y])
{
queue.Enqueue(next);
cameFrom[next] = current;
}
}
}
// 如果终点不可达,返回空列表
if (!cameFrom.ContainsKey(end))
return MoveAttackType.None;
// 从终点回溯路径
path = new List<Vector2Int>();
for (var at = end; at != start; at = cameFrom[at])
{
path.Add(at);
}
path.Add(start);
path.Reverse(); // 翻转为从起点到终点的顺序
return MoveAttackType.Move;
}
public void Upgrade(MapData mapData, UnitData u)
{
/*
u.veteran = true;
u.health = u.maxHealth + 5;
RenderUpdateManager.Instance.AddUnitRUList(u.unitId);
*/
}
//unit[uid]原地休息
public void Recover(MapData mapData, UnitData unitData) { }
//unit[uid]从陆地进入港口变为boat
public void LandToBoat(MapData mapData, UnitData unitData)
{
unitData.CarryUnitFullType = unitData.UnitFullType;
if (unitData.UnitType == UnitType.BigGuy)
Main.UnitLogic.UnitTypeTransform(mapData, unitData, UnitType.Juggernaut);
else if (unitData.UnitType == UnitType.KaguyaFrenchWolf)
Main.UnitLogic.UnitTypeTransform(mapData, unitData, UnitType.WolfJuggernaut);
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;
}
//unit[uid]从港口进入陆地,变为原来的单位
public void BoatToLand(MapData mapData, UnitData unitData)
{
Main.UnitLogic.UnitTypeTransform(mapData,unitData, unitData.CarryUnitType, unitData.CarryGiantType,unitData.CarryUnitLevel);
unitData.AP = 0;
unitData.MP = 0;
unitData.CP = 0;
}
public bool HeroUpgrade(MapData map,UnitData unit)
{
var type = unit.UnitFullType;
if (type.UnitType != UnitType.Giant) return false;
if (type.GiantType == GiantType.None) return false;
if (type.UnitLevel >= 3) return false;
var targetType = new UnitFullType()
{ UnitType = unit.UnitType, GiantType = unit.GiantType, UnitLevel = unit.UnitLevel + 1 };
UnitTypeTransform(map,unit,targetType);
unit.Player(map)?.PlayerHeroData.UpdateHeroMaxCount(targetType.UnitLevel);
return unit.Player(map)?.PlayerHeroData.UpdateHero(map,unit.Player(map),unit.UnitFullType) ?? false;
}
//unitData开启下一回合
public void StartNextTurn(MapData mapData, UnitData unitData)
{
if (!unitData.IsAlive())
return;
unitData.AP = 1;
unitData.MP = 1;
unitData.CP = 1;
unitData.Renderer(mapData)?.InstantUpdateUnit(true);
}
public void UnitEndTurn(MapData mapData, UnitData unitData)
{
if (!unitData.IsAlive())
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.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Heal));
unitData.Renderer(mapData)?.InstantUpdateUnit(true);
}
}
}
public void UnitTypeTransform(MapData mapData, UnitData unitData, UnitFullType targetType)
{
UnitTypeTransform(mapData, unitData, targetType.UnitType, targetType.GiantType, targetType.UnitLevel);
}
//unitData转换成另一个UnitType
public void UnitTypeTransform(MapData mapData, UnitData unitData, UnitType targetType,
GiantType giantType = GiantType.None, uint unitLevel = 0)
{
//Debug.Log($"Check Transform Before APMPCP = {unitData.AP} , {unitData.MP} ,{unitData.CP} ");
//重新处理一下:
//生命周期: 1 . 卸载所有原来的skill
//TODO 要迭代这块主要是在skill那边要加标记确认一个skill是否可以继承是否transfrom的时候要删掉之类的要不要触发情况等等
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;
var oldSkills = new List<SkillBase>();
var originSkills = new List<SkillType>() { };
foreach (var t in unitData.Skills) originSkills.Add(t.GetSkillType());
foreach (var skilltype in originSkills)
{
if (targetInfo.Skills.Contains(skilltype)) continue;
unitData.GetSkill(skilltype, out var skill);
if (skill == null) continue;
var fullType = new UnitFullType(targetType, giantType, unitLevel);
if (skill.ReservedOnTransform(unitData, fullType)) continue;
oldSkills.Add(skill);
unitData.RemoveSkill(skilltype, mapData);
}
foreach (var skillType in targetInfo.Skills)
{
unitData.AddInitSkill(skillType, mapData);
unitData.GetSkill(skillType, out var newSkill);
newSkill?.NewSkillOnTransform(oldSkills);
}
unitData.UnitFullType.UnitType = targetType;
unitData.UnitFullType.GiantType = giantType;
unitData.UnitFullType.UnitLevel = unitLevel;
//Debug.Log($"Check Transform After APMPCP = {unitData.AP} , {unitData.MP} ,{unitData.CP} ");
//遍历所有techAtom,填加科技技能
foreach (var atom in unitData.Player(mapData).TechTree.TechAtomCacheSet)
{
if(!Table.Instance.TechDataAssets.GetTechAtomInfo(atom,out var info))continue;
if (!info.IsAddSkill) continue;
if (!info.CheckCondition(unitData.UnitFullType)) continue;
unitData.AddInitSkill(info.AddSkillType, mapData);
unitData.GetSkill(info.AddSkillType, out var newSkill);
newSkill?.NewSkillOnTransform(oldSkills);
}
if (mapData.GetPlayerDataByUnitId(unitData.Id, out var playerData))
{
foreach (var kv in playerData.PlayerHeroData.HeroTaskDict)
kv.Value.OnTransformUnit(mapData, playerData, unitData);
}
}
public void PassiveMoveAway(MapData mapData,UnitData unitData)
{
if (!mapData.GetGridDataByUnitId(unitData.Id, out var gridData))
return;
var unitGrid = unitData.Grid(mapData);
if (unitGrid == null) return;
//默认向远离最近城市的方向推出
if(!mapData.GetNearestCity(unitData,out var city))return;
var cityGrid = city.Grid(mapData);
if (cityGrid == null) return;
GridData bestGrid = null;
int score = -1;
var gridList = mapData.GridMap.GetAroundGridData(1,1,gridData);
foreach (var targetGrid in gridList)
{
if (targetGrid == gridData)
continue;
if (!CheckUnitAbleForGrid_RealTimeStatus(mapData, unitData, targetGrid))
continue;
var tmpScore = mapData.GridMap.CalcManhattanDistance(targetGrid, cityGrid);
//TODO MORIYAKNIGHT 特判
if (unitData.UnitType == UnitType.MoriyaKnight && targetGrid.Feature == TerrainFeature.Mountain)
{
tmpScore += 100000;
}
if (score == -1 || tmpScore > score)
{
bestGrid = targetGrid;
score = tmpScore;
}
}
if (score != -1)
{
//MoveToLogic(mapData,unitData,bestGrid,MoveType.PassiveMove);
var param = new CommonActionParams(mapData, playerData:unitData.Player(mapData), unitData:unitData, gridData:bestGrid);
var moveId = new CommonActionId { ActionType = CommonActionType.UnitPassiveMove};
var moveAction = new UnitPassiveMoveAction(moveId);
param.RefreshParams();
moveAction.ExecuteWithoutFullActionPeriod(param);
}
else
{
var data = new FragmentUnitData(FragmentType.Die, unitData.Renderer(mapData),unitGrid,city);
PresentationManager.EnqueueTask(new FragmentSequencerTask(new FragmentDie(data)));
Main.UnitLogic.UnitUnnaturalDie(mapData,unitData);
}
}
//判断一个unit 目前是否能直接站在那个grid上要考虑grid目前的状态例如盟友的城市中心有人的格子 等等)
public bool CheckUnitAbleForGrid_RealTimeStatus(MapData mapData, UnitData unitData, GridData gridData)
{
//TODO 这里一定要改了用把landType通过unitSkill改过来 ,或者至少完成一套完备的方案
if (!mapData.CheckLandTypeForGrid(unitData.UnitFullType, gridData))
return false;
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 (unitData.GetLandType() == LandType.Fly)
return true;
//如果是水域
if (gridData.Terrain != TerrainType.Land)
{
if (gridData.Resource == ResourceType.Bridge)
{
return true;
}
//如果本身就是水生单位
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.Port)
{
if (unitInfo.LandType != LandType.WaterAndAshore
&& unitInfo.LandType == LandType.WaterOnly)
return false;
var gridPlayer = gridData.Player(mapData);
var unitPlayer = unitData.Player(mapData);
if (gridPlayer == null || unitPlayer == null) return false;
if(!mapData.SameUnion(gridPlayer.Id,unitPlayer.Id)) 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;
}
//判断一个unit是否有能力去某个grid但是和那个grid目前的状态无关
public bool CheckUnitAbleForGrid_OfflineStatus(MapData map, PlayerData self, UnitData unit, GridData grid )
{
if (unit != null)
{
if (!map.CheckLandTypeForGrid(unit.UnitFullType, grid))
return false;
}
if (grid.Resource == ResourceType.Bridge)
return true;
var landType = unit?.GetLandType() ?? LandType.LandAndWater;
//step #1 排除科技情况
if (landType != LandType.Fly)
{
if (grid.Feature == TerrainFeature.Mountain && !self.TechTree.TechAtomCacheSet.Contains(TechAtom.UnitSkillMOUNTAINMOVE)) return false;
if (grid.Terrain == TerrainType.ShallowSea && !self.TechTree.TechAtomCacheSet.Contains(TechAtom.UnitSkillWATERMOVE)) return false;
if (grid.Terrain == TerrainType.DeepSea && !self.TechTree.TechAtomCacheSet.Contains(TechAtom.UnitSkillOCEANMOVE)) 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.WaterOnly)
{
if (grid.Terrain == TerrainType.Land) return false;
}
if (landType == LandType.LandOnly)
{
if (grid.Terrain != TerrainType.Land) return false;
}
return true;
}
// 仅通过landType判断一个unit是否有能力从gridA移动到相邻的gridB不考虑grid上面是否有单位阻挡如果unit==null 则用player的通用能力来判断
public bool CheckUnitCanMoveToNearbyGridOnlyByLandType(MapData map, PlayerData self, UnitData unit,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;
bool startCheck = CheckUnitAbleForGrid_OfflineStatus(map, self, unit, startGrid);
bool endCheck = CheckUnitAbleForGrid_OfflineStatus(map, self, unit, endGrid);
if (!startCheck || !endCheck) return false;
//如果是判断一个具体的unit
if (unit != null)
{
switch(unit.GetLandType())
{
case LandType.Fly:
return true;
break;
case LandType.LandAndPort:
return true;
break;
case LandType.LandAndWater:
return true;
break;
case LandType.LandOnly:
return true;
break;
case LandType.WaterAndAshore:
return true;
break;
case LandType.WaterOnly:
return true;
break;
default:
break;
}
}
//如果是判断player
else
{
return true;
}
return true;
}
}
}