1252 lines
59 KiB
C#
1252 lines
59 KiB
C#
/*
|
||
* @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.Skill;
|
||
using Logic.Timeline;
|
||
using UnityEngine;
|
||
using RuntimeData;
|
||
using TH1_Logic.Core;
|
||
using TH1Renderer;
|
||
using Unity.VisualScripting;
|
||
|
||
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 CityData DamageTargetCity;
|
||
//伤害值
|
||
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)
|
||
{
|
||
//处理移动目标有单位的情况(return false)
|
||
var targetGridUnit = gridData.Unit(mapData);
|
||
if (targetGridUnit != null) return false;
|
||
|
||
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 && mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(gridData.Id)) 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(mapData);
|
||
/*if (gridData.Feature == TerrainFeature.Mountain)
|
||
radius = radius < 2 ? 2 : radius;*/
|
||
var count = Main.PlayerLogic.UpdateSight(mapData,playerData,mapData.GridMap.GetAroundGridIdList(radius,gridData));
|
||
unitData.HeroTask(mapData)?.OnExploredGrids(mapData, count);
|
||
|
||
//更新所有人的外交好感情况。临时用的。必须真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);
|
||
}
|
||
|
||
|
||
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;
|
||
}
|
||
|
||
public bool AttackAlly(MapData mapData, UnitData unit1, UnitData unit2)
|
||
{
|
||
//只有白名单才能使用AttackAlly
|
||
//处理Eirin的友军攻击
|
||
if (unit1.GetSkill(SkillType.EIRINFRENCHATTACK, out var _))
|
||
{
|
||
MapRenderer.Instance.ProjectileManager.CreateProjectile(unit1.GetPosition(mapData) + Vector3.up * 3f,unit2.GetPosition(mapData) + Vector3.up * 3f,ProjectileType.Arrow,ProjectileMoveType.Parabola);
|
||
unit1.ClearAPMPCP();
|
||
unit1.RenderMark = true;
|
||
int recover = 5;
|
||
if (unit1.GetSkill(SkillType.EIRINFRENCHBUFF, out var _) && unit2.GetUnitFullType.UnitType == UnitType.Giant)
|
||
recover = 15;
|
||
Timer.Instance.TimerRegister(this, () =>
|
||
{
|
||
var before = unit1.Health;
|
||
RecoverHealth(mapData, unit1, unit2, recover);
|
||
unit2.AddSkill(SkillType.KAGUYAFRENCHSYNERGY,1);
|
||
|
||
//如果溢出
|
||
if (unit1.Health - before < recover)
|
||
{
|
||
unit2.AddSkill(SkillType.MOVERANGEUP,1);
|
||
}
|
||
},0.3f,"UnitLogic AttackAlly");
|
||
return true;
|
||
}
|
||
//处理Kaguya的友军攻击
|
||
if (unit1.GetSkill(SkillType.KAGUYAFRENCHATTACK, out var _) || unit1.GetSkill(SkillType.KAGUYAFRENCHATTACKPRO, out var _) )
|
||
{
|
||
var lv = unit1.GetSkill(SkillType.KAGUYAFRENCHATTACKPRO, out var _) ? 9 : 1;
|
||
MapRenderer.Instance.ProjectileManager.CreateProjectile(unit1.GetPosition(mapData) + Vector3.up * 2f,unit2.GetPosition(mapData) + Vector3.up * 2f,ProjectileType.KaguyaAttack,ProjectileMoveType.Straight);
|
||
unit1.ClearAPMPCP();
|
||
unit1.RenderMark = true;
|
||
unit2.AddSkill(SkillType.KAGUYAFRENCHFOREVERBUFF);
|
||
if(unit2.GetSkill(SkillType.KAGUYAFRENCHFOREVERBUFF, out var skill))
|
||
skill.AddLevel(mapData,unit1,lv);
|
||
|
||
Timer.Instance.TimerRegister(this, () =>
|
||
{
|
||
var t = new GridVFXRenderMark(GridVFXType.KaguyaFrenchBuff);
|
||
unit2.Grid(mapData)?.SetGridVFXRenderMark(t);
|
||
},0.3f,"UnitLogic AttackAlly");
|
||
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
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;
|
||
|
||
foreach (var skill in unit1.Skills) skill.BeforeSelfActiveAttack(mapData, unit1, unit2);
|
||
// 计算攻击伤害
|
||
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.CalcCounterDamage(mapData, unit1,unit2 );
|
||
//攻击会消耗所有类别的行动点数
|
||
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.FrenchEirin)
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.Arrow;
|
||
|
||
//帕秋莉的攻击走帕秋莉特效
|
||
if (unit1.GiantType == GiantType.EgyptianPatchouli)
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.PatchouliAttack;
|
||
//妹红的远程攻击走妹红攻击
|
||
if (unit1.GiantType == GiantType.FrenchMokou)
|
||
{
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.MokouAttack;
|
||
//TODO 处理妹红近战和远战的问题
|
||
/*if(mapData.GridMap.CalcDistance(grid1,grid2) > 1)
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.MokouAttack;
|
||
else
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.Melee;*/
|
||
}
|
||
//铃仙攻击走铃仙特效
|
||
if (unit1.GetUnitFullType.GiantType == GiantType.FrenchReisen || unit1.GetUnitFullType.UnitType == UnitType.KaguyaFrenchReisenIllusion)
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.ReisenAttack;
|
||
//辉夜走辉夜攻击特效
|
||
if (unit1.GetUnitFullType.GiantType == GiantType.FrenchKaguya)
|
||
unit1.AttackRenderMarkAttackAnimType = AttackAnimType.KaguyaAttack;
|
||
/*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 !!! 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;
|
||
}
|
||
|
||
if (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;
|
||
|
||
//TODO 后面迭代 目前临时处理
|
||
if (target.GetSkill(SkillType.REISENILLUSIONPRO, out var _) ||
|
||
target.GetSkill(SkillType.REISENILLUSION, out var _))
|
||
{
|
||
target.Health = 1;
|
||
settlement.DamageValue = dmg = 1;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
// unit 受伤和伤害他人回调
|
||
origin.OnDamageOther(mapData, settlement);
|
||
target.OnDamaged(mapData, settlement);
|
||
mapData.OnUnitDamaged(settlement);
|
||
|
||
if (type is DamageType.Splash or 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);
|
||
}
|
||
|
||
return settlement;
|
||
}
|
||
|
||
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.VFXRenderMarkHeal = true;
|
||
target.RenderMark = true;
|
||
origin.RenderMark = true;
|
||
return realRecover;
|
||
}
|
||
|
||
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 )
|
||
{
|
||
if (!map.CheckLandTypeForGrid(unit.GetUnitFullType, grid))
|
||
return false;
|
||
|
||
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() is LandType.LandAndPort)
|
||
{
|
||
MoveInfoCostMap[i, j] = 1;
|
||
if (targetGridData.Vegetation == Vegetation.Trees)
|
||
{
|
||
MoveInfoCostMap[i, j] = 3;
|
||
if (unitData.GetSkill(SkillType.BAMBOOMOVE, out var _))
|
||
MoveInfoCostMap[i, j] = 1;
|
||
}
|
||
if (targetGridData.Feature == TerrainFeature.Mountain)
|
||
MoveInfoCostMap[i, j] = playerData.TechTree.CheckIfHasTech(TechType.Climbing) ? 3 : -1;
|
||
if (targetGridData.Terrain == TerrainType.ShallowSea)
|
||
{
|
||
if (targetGridData.Resource == ResourceType.Bridge)
|
||
MoveInfoCostMap[i, j] = 1;
|
||
//如果是port,要判断是盟军的port 还是其他的port
|
||
else if (targetGridData.Resource == ResourceType.Port)
|
||
{
|
||
//如果没有port科技,不能去
|
||
if (!playerData.TechTree.CheckIfHasTech(TechType.Fishing))
|
||
MoveInfoCostMap[i, j] = -1;
|
||
//如果是己方的port
|
||
else if (mapData.GetPlayerDataByTerritoryGridId(targetGridData.Id, out var targetGridPlayer)
|
||
&& mapData.SameUnion(targetGridPlayer.Id, playerData.Id))
|
||
MoveInfoCostMap[i, j] = 999;
|
||
else
|
||
MoveInfoCostMap[i, j] = -1;
|
||
}
|
||
else MoveInfoCostMap[i, j] = -1;
|
||
}
|
||
|
||
if (targetGridData.Terrain == TerrainType.DeepSea)
|
||
MoveInfoCostMap[i, j] = -1;
|
||
}
|
||
else if (unitData.GetLandType() is LandType.LandAndWater)
|
||
{
|
||
MoveInfoCostMap[i, j] = 1;
|
||
if (targetGridData.Vegetation == Vegetation.Trees)
|
||
{
|
||
MoveInfoCostMap[i, j] = 3;
|
||
if (unitData.GetSkill(SkillType.BAMBOOMOVE, out var _))
|
||
MoveInfoCostMap[i, j] = 1;
|
||
}
|
||
|
||
if (targetGridData.Feature == TerrainFeature.Mountain)
|
||
MoveInfoCostMap[i, j] = playerData.TechTree.CheckIfHasTech(TechType.Climbing) ? 3 : -1;
|
||
if (targetGridData.Terrain == TerrainType.ShallowSea)
|
||
{
|
||
MoveInfoCostMap[i, j] = 1;
|
||
if (unitData.GiantType == GiantType.FrenchInaba)
|
||
MoveInfoCostMap[i, j] = 2;
|
||
//如果没有WATERMOVE
|
||
if (!unitData.GetSkill(SkillType.WATERMOVE, out var _))
|
||
MoveInfoCostMap[i, j] = -1;
|
||
|
||
}
|
||
|
||
if (targetGridData.Terrain == TerrainType.DeepSea)
|
||
{
|
||
MoveInfoCostMap[i, j] = 1;
|
||
if (unitData.GiantType == GiantType.FrenchInaba)
|
||
MoveInfoCostMap[i, j] = 2;
|
||
if (!unitData.GetSkill(SkillType.OCEANMOVE, out var _))
|
||
MoveInfoCostMap[i, j] = -1;
|
||
}
|
||
|
||
}
|
||
else if (unitData.GetLandType() is LandType.LandOnly)
|
||
{
|
||
MoveInfoCostMap[i, j] = 1;
|
||
if (targetGridData.Vegetation == Vegetation.Trees)
|
||
{
|
||
MoveInfoCostMap[i, j] = 3;
|
||
if (unitData.GetSkill(SkillType.BAMBOOMOVE, out var _))
|
||
MoveInfoCostMap[i, j] = 1;
|
||
}
|
||
|
||
if (targetGridData.Feature == TerrainFeature.Mountain)
|
||
MoveInfoCostMap[i, j] = playerData.TechTree.CheckIfHasTech(TechType.Climbing) ? 3 : -1;
|
||
if (targetGridData.Terrain is TerrainType.ShallowSea or TerrainType.DeepSea)
|
||
{
|
||
if (targetGridData.Resource == ResourceType.Bridge)
|
||
MoveInfoCostMap[i, j] = 1;
|
||
else
|
||
MoveInfoCostMap[i, j] = -1;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
//如果是水上单位
|
||
else if (unitData.GetLandType() is LandType.WaterAndAshore or LandType.WaterOnly)
|
||
{
|
||
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 if (unitData.GetLandType() is LandType.Fly)
|
||
{
|
||
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 (mapData.SameUnion(playerDataB.Id ,playerData.Id)) 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;
|
||
}
|
||
MoveInfoCostMap[gridDataB.Pos.X,gridDataB.Pos.Y] = 999;
|
||
}
|
||
|
||
//将所有友军的城市中心设置为-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;
|
||
MoveInfoCostMap[tgrid.Pos.X,tgrid.Pos.Y] = -1;
|
||
}
|
||
|
||
//初始化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;
|
||
//如果是陆地单位,看是否需要减去道路或者桥梁的行动力加成。道路是非敌人领地才行,并且目标不是耗光所有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;
|
||
|
||
//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<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,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);
|
||
//如果目标位置上面有友军单位,除非有特殊技能,否则是none
|
||
if (mapData.SameUnionOrJustBreakUnion(playerDataA.Id, playerDataB.Id))
|
||
{
|
||
//处理Eirin技能 必须是盟友,不能是刚解除结盟的友军
|
||
if (mapData.SameUnion(playerDataA.Id, playerDataB.Id))
|
||
{
|
||
if (unitDataA.GetSkill(SkillType.EIRINFRENCHATTACK, out var _))
|
||
return MoveAttackType.Ally;
|
||
}
|
||
//处理Kaguya技能 必须是盟友,不能是刚解除结盟的友军
|
||
if (mapData.SameUnion(playerDataA.Id, playerDataB.Id))
|
||
{
|
||
if (unitDataA.GetSkill(SkillType.KAGUYAFRENCHATTACK, out var _) || unitDataA.GetSkill(SkillType.KAGUYAFRENCHATTACKPRO, out var _))
|
||
return MoveAttackType.Ally;
|
||
}
|
||
return MoveAttackType.None;
|
||
}
|
||
|
||
//如果目标位置上面有敌军单位,那不可移动,但是有可能可以攻击
|
||
else if (mapData.GridMap.CalcDistance(gridDataA, gridDataB) <= unitDataA.GetAttackRange() &&
|
||
!unitDataA.IsLimitSelfAttack(mapData)) //判断距离在攻击距离范围内,且具备攻击能力
|
||
{
|
||
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;
|
||
}
|
||
|
||
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]从陆地进入港口,变为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;
|
||
var count = Main.PlayerLogic.UpdateSight(mapData,player,mapData.GridMap.GetAroundGridIdList(unitData.GetSightRange(mapData),grid));
|
||
unitData.HeroTask(mapData)?.OnExploredGrids(mapData, count);
|
||
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;
|
||
}
|
||
|
||
public bool HeroUpgrade(MapData map,UnitData unit)
|
||
{
|
||
var type = unit.GetUnitFullType;
|
||
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(unit.GetUnitFullType) ?? false;
|
||
}
|
||
|
||
//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;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
//TODO 要迭代这块,主要是在skill那边要加标记,确认一个skill是否可以继承,是否transfrom的时候要删掉之类的,要不要触发情况等等
|
||
bool nopop = unitData.GetSkill(SkillType.NOPOPULATION,out var _);
|
||
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 ski))
|
||
ski.OnTransformRemoved(unitData,mapData);
|
||
unitData.AddSkill(skilltype);
|
||
}
|
||
unitData.UnitType = targetType;
|
||
unitData.GiantType = giantType;
|
||
unitData.UnitLevel = unitLevel;
|
||
|
||
//处理AddSkill的生命周期
|
||
foreach(var skill in unitData.Skills)
|
||
skill.OnSkillAdd(unitData,mapData);
|
||
|
||
if(nopop)
|
||
unitData.AddSkill(SkillType.NOPOPULATION);
|
||
}
|
||
|
||
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 (!CheckUnitCanGoOnGrid(mapData, unitData, targetGrid))
|
||
continue;
|
||
if (score == -1 || mapData.GridMap.CalcManhattanDistance(targetGrid, cityGrid) > score)
|
||
{
|
||
bestGrid = targetGrid;
|
||
score = mapData.GridMap.CalcManhattanDistance(targetGrid, cityGrid);
|
||
}
|
||
|
||
}
|
||
if(score != -1)
|
||
MoveTo(mapData,unitData,bestGrid,MoveType.PassiveMove);
|
||
else
|
||
Die(mapData,unitData);
|
||
}
|
||
|
||
public bool CheckUnitCanGoOnGrid(MapData mapData, UnitData unitData, GridData gridData)
|
||
{
|
||
|
||
//TODO 这里一定要改了,用把landType通过unitSkill改过来 ,或者至少完成一套完备的方案
|
||
if (!mapData.CheckLandTypeForGrid(unitData.GetUnitFullType, 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 (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;
|
||
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;
|
||
}
|
||
|
||
}
|
||
}
|