TH1/Unity/Assets/Scripts/TH1_Logic/Action/PlayerActionLogic.cs

1022 lines
48 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月10日 星期四 11:04:44
* @Modify:
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Logic;
using Logic.Action;
using Logic.CrashSight;
using Logic.Skill;
using RuntimeData;
using TH1_Core.Events;
using TH1_Core.Managers;
using TH1_Logic.Core;
using TH1Renderer;
using Unity.VisualScripting.Antlr3.Runtime.Tree;
using UnityEngine;
//这里是所有BuildAction派生子类的实现模块
namespace TH1_Logic.Action
{
public enum PlayerActionType
{
None,
OfferAlly,
AcceptAlly,
RefuseAlly,
BreakAlly,
Embassy,
SelectHero,
FinishHeroTask,
BreakEmbassy,
SelectTreasureOptionA,
SelectTreasureOptionB,
TreasureGainCoin,
TreasureGainUnit,
TreasureGainTech,
TreasureGainCulture,
TreasureGainCityExp,
DanegeldDemand,
DanegeldPay,
DanegeldReject,
Max
}
//UnitAction单位行动逻辑类
public class PlayerActionAction : ActionLogicBase
{
public PlayerActionAction(CommonActionId id) : base(id)
{
}
protected override bool Execute(CommonActionParams actionParams)
{
return false;
}
public override bool CheckCan(CommonActionParams actionParams)
{
return false;
}
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
{
showType = ShowType.None;
return false;
}
protected bool PlayerActionCheckBaseInfo(CommonActionParams actionParam)
{
//鲁棒性判断必须包含playerData表明是哪个玩家正在操作哪个单位
if (actionParam.PlayerData == null) return false;
return true;
}
}
//UnitAction单位行动逻辑类
public class PlayerActionDiplomacy : PlayerActionAction
{
public PlayerActionDiplomacy(CommonActionId id) : base(id)
{
}
protected override bool Execute(CommonActionParams actionParams)
{
//Step #1 鲁棒性判断
if (actionParams.PlayerData == null) return false;
if (actionParams.MainObjectType != MainObjectType.Player) return false;
if (actionParams.TargetPlayerData == null) return false;
if (!CheckCan(actionParams)) return false;
MapData map = actionParams.MapData;
PlayerData player = actionParams.PlayerData;
PlayerData targetPlayer = actionParams.TargetPlayerData;
if (IsDanegeldAction(_actionId.PlayerActionType))
return ExecuteDanegeld(actionParams, player, targetPlayer);
if (!map.GetCapitalCityDataByPlayerId(actionParams.TargetPlayerData.Id, out var targetCity)) return false;
if (!map.GetCapitalCityDataByPlayerId(actionParams.PlayerData.Id, out var city)) return false;
if (!map.GetGridDataByCityId(targetCity.Id, out var targetGrid)) return false;
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
actionParams.TargetPlayerData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
//Step #2 处理大使馆
if (_actionId.PlayerActionType == PlayerActionType.Embassy)
{
//Step #1建立大使馆,
dipInfo.IsEmbassy = true;
//如果是真人玩家 在其他首都建立大使馆,更新对应城市的视觉,播放大使馆建立特效
if (actionParams.MapData == Main.MapData && player.Id == Main.MapData.PlayerMap.SelfPlayerId)
{
targetCity.SetCityRenderer(actionParams.MapData);
targetGrid.Renderer(actionParams.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Treasure));
}
//如果是真人玩家 AI在真人玩家的首都建立大使馆更新对应城市的视觉,
if (actionParams.MapData == Main.MapData && targetPlayer.Id == Main.MapData.PlayerMap.SelfPlayerId)
{
city.SetCityRenderer(actionParams.MapData);
}
//Step #2 如果对方还没有meet我加入meetlist
if(!targetPlayer.MeetPlayers.Contains(player.Id))
Main.PlayerLogic.AddMeetPlayer(map,targetPlayer,player);
//Step #3 更新大使馆周围一格的视野,会自动处理视觉部分
var gidList = map.GridMap.GetAroundGridIdList(1,targetGrid,true );
Main.PlayerLogic.UpdateSight_LogicView(map,player,gidList);
//step #4 扣钱
player.SpendCoin(GetCost(actionParams));
//Step #5 如果是真人玩家发起弹出一个announce,并关闭当前info
if (player.Id == Main.MapData.PlayerMap.SelfPlayerId)
{
var announcement = new ShowUIAnnounceDiplomacy { PlayerActionType = PlayerActionType.Embassy,PlayerId = player.Id,TargetPlayerId = targetPlayer.Id };
EventManager.Publish(announcement);
EventManager.Publish(new HideUIInfoDiplomacy());
}
//Step #6 如果是其他玩家发起但是目标是真人玩家弹出一个announce,并关闭当前info
if (targetPlayer.Id == Main.MapData.PlayerMap.SelfPlayerId)
{
var announcement = new ShowUIAnnounceDiplomacy { PlayerActionType = PlayerActionType.Embassy,PlayerId = player.Id,TargetPlayerId = targetPlayer.Id };
EventManager.Publish(announcement);
EventManager.Publish(new HideUIInfoDiplomacy());
}
return true;
}
//Step #3 处理申请结盟
if (_actionId.PlayerActionType == PlayerActionType.OfferAlly)
{
//Step #1 让对方收到我方邮件
dipInfo2.IsLeagueRequest = true;
//Step #2 如果对方还没有meet我加入meetlist
if(!targetPlayer.MeetPlayers.Contains(player.Id))
Main.PlayerLogic.AddMeetPlayer(map,targetPlayer,player);
//Step #3 如果是真人玩家发起弹出一个notify,并关闭当前info
if (Main.MapData == actionParams.MapData && player.Id == Main.MapData.PlayerMap.SelfPlayerId)
{
EventManager.Publish(new ShowUINotifyCommon(){UINotifyCommonType = UINotifyCommonType.DiplomacyOfferAlly});
EventManager.Publish(new HideUIInfoDiplomacy());
}
//Step #4 如果是真人玩家收到,那么要到下回合开始的时候才会触发
return true;
}
//Step #4 处理accept结盟
if (_actionId.PlayerActionType == PlayerActionType.AcceptAlly)
{
//Step #1 关闭我方的对方结盟信件请求标记,登记为盟友
dipInfo.IsLeagueRequest = false;
//如果我方也给对方发邮件,也关闭
dipInfo2.IsLeagueRequest = false;
dipInfo.DiplomacyState = DiplomacyState.League;
dipInfo2.DiplomacyState = DiplomacyState.League;
//Step #2 如果target是真map真人玩家 弹出一个presentationUI 提示达成盟友更新双方首都的cityinfo
if (map == Main.MapData && targetPlayer.Id == map.PlayerMap.SelfPlayerId)
{
var announcement = new ShowUIAnnounceDiplomacy { PlayerActionType = PlayerActionType.AcceptAlly,PlayerId = player.Id,TargetPlayerId = targetPlayer.Id };
EventManager.Publish(announcement);
city.SetCityRenderer(actionParams.MapData);
targetCity.SetCityRenderer(actionParams.MapData);
}
//Step #3 如果是真map我方是真人玩家更新双方首都的cityinfo
if (map == Main.MapData && player.Id == map.PlayerMap.SelfPlayerId)
{
city.SetCityRenderer(actionParams.MapData);
targetCity.SetCityRenderer(actionParams.MapData);
}
//Step #4 如果是真map双方有一个是真人玩家就要处理的视觉情况
if (map == Main.MapData && (player.Id == map.PlayerMap.SelfPlayerId) ||
(targetPlayer.Id == map.PlayerMap.SelfPlayerId))
{
foreach (var tunit in map.UnitMap.UnitList)
{
if(!map.GetPlayerDataByUnitId(tunit.Id,out var uplayer))continue;
if (uplayer.Id == map.PlayerMap.SelfPlayerId) continue;
if (uplayer.Id == targetPlayer.Id || uplayer.Id == player.Id)
tunit.Renderer(map)?.InstantUpdateUnit(true);
}
city.SetCityRenderer(actionParams.MapData);
targetCity.SetCityRenderer(actionParams.MapData);
EventManager.Publish(new HideUIInfoDiplomacy());
}
return true;
}
//Step #5 处理refuse结盟
if (_actionId.PlayerActionType == PlayerActionType.RefuseAlly)
{
//Step #1 关闭对方的结盟请求标记
dipInfo.IsLeagueRequest = false;
//Step #2 如果target是真map真人玩家 弹出一个presentationUI 提示,明确拒绝对方
if (map == Main.MapData && targetPlayer.Id == map.PlayerMap.SelfPlayerId)
{
var announcement = new ShowUIAnnounceDiplomacy { PlayerActionType = PlayerActionType.RefuseAlly,PlayerId = player.Id ,TargetPlayerId = targetPlayer.Id };
EventManager.Publish(announcement);
}
return true;
}
//Step #6 处理break结盟
if (_actionId.PlayerActionType == PlayerActionType.BreakAlly)
{
if (dipInfo.IsTeammate || dipInfo2.IsTeammate) return false;
//Step #1 设置双方的外交状态为背盟
dipInfo.DiplomacyState = DiplomacyState.LeagueRupture;
dipInfo2.DiplomacyState = DiplomacyState.LeagueRupture;
var disbandList = new List<UnitData>();
//Step #2 所有单位跳过本回合,所有对方领土的单位自动解散
foreach (var unit in map.UnitMap.UnitList)
{
if(!map.GetPlayerDataByUnitId(unit.Id,out var uplayer))continue;
if(uplayer.Id != player.Id) continue;
if (!map.GetGridDataByUnitId(unit.Id, out var ugrid)) continue;
var gridHasPlayer = map.GetPlayerDataByTerritoryGridId(ugrid.Id, out var ugridPlayer);
//如果是对方领土上的己方unit强制解散
if (gridHasPlayer && ugridPlayer.Id == targetPlayer.Id)
{
//加入解散列表。不能就地解散因为会影响到map.UnitMap.UnitList
disbandList.Add(unit);
}
else
{
unit.ClearActionPoint();
unit.Renderer(map)?.InstantUpdateUnit(true);
}
}
foreach (var unit in disbandList)
{
var action = ActionLogicFactory.GetActionLogic(new CommonActionId()
{ ActionType = CommonActionType.UnitAction, UnitActionType = UnitActionType.ForceDisband });
var param = new CommonActionParams(mapData: map, playerData:player,unitData: unit, mainObjectType: MainObjectType.Unit);
action?.CompleteExecute(param);
}
//Step #3 如果target是真map真人玩家 弹出一个presentationUI 提示,明确背叛对方,更新双方cityinfo
if (map == Main.MapData && targetPlayer.Id == map.PlayerMap.SelfPlayerId)
{
var announcement = new ShowUIAnnounceDiplomacy { PlayerActionType = PlayerActionType.BreakAlly,PlayerId = player.Id,TargetPlayerId = targetPlayer.Id };
EventManager.Publish(announcement);
}
//Step #4 如果是真map双方有一个是真人玩家就要处理的视觉情况
if (map == Main.MapData && (player.Id == map.PlayerMap.SelfPlayerId) ||
(targetPlayer.Id == map.PlayerMap.SelfPlayerId))
{
foreach (var tunit in map.UnitMap.UnitList)
{
if(!map.GetPlayerDataByUnitId(tunit.Id,out var uplayer))continue;
if (uplayer.Id == map.PlayerMap.SelfPlayerId) continue;
if (uplayer.Id == targetPlayer.Id || uplayer.Id == player.Id)
tunit.Renderer(map)?.InstantUpdateUnit(true);
}
city.SetCityRenderer(actionParams.MapData);
targetCity.SetCityRenderer(actionParams.MapData);
EventManager.Publish(new HideUIInfoDiplomacy());
//关闭当前的场上的高亮
Main.Instance.MapInteractionLogic.CancelAllHighlight();
}
return true;
}
return false;
}
public override bool CheckCan(CommonActionParams actionParams)
{
//Step #1 鲁棒性判断
if (actionParams.PlayerData == null || !actionParams.PlayerData.IsSurvival) return false;
if (actionParams.MainObjectType != MainObjectType.Player) return false;
if (actionParams.TargetPlayerData == null || !actionParams.TargetPlayerData.IsSurvival ) return false;
if (!actionParams.PlayerData.MeetPlayers.Contains(actionParams.TargetPlayerData.Id)) return false;
if (IsDanegeldAction(_actionId.PlayerActionType))
return CheckCanDanegeld(actionParams);
//Step #2 判断钱够不够
if (GetCost(actionParams) > actionParams.PlayerData.PlayerCoin)
return false;
//Step #3 如果不具备科技 return
if (!actionParams.PlayerData.TechTree.CheckActionCan(_actionId))
return false;
//Step #4 处理大使馆
if (_actionId.PlayerActionType == PlayerActionType.Embassy)
{
//如果对方没有原始首都,不可建立大使馆
actionParams.MapData.GetCapitalCityDataByPlayerId(actionParams.TargetPlayerData.Id,out var city);
if (!Main.CityLogic.CheckCradleCapital(actionParams.MapData, actionParams.TargetPlayerData, city))
return false;
//如果已经在对方建立了大使馆,不可再次建立大使馆
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
if (dipInfo.IsEmbassy) return false;
return true;
}
//Step #5 判断能否申请结盟
if (_actionId.PlayerActionType == PlayerActionType.OfferAlly)
{
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
//如果刚解除结盟或者已经结盟 return
if (dipInfo.DiplomacyState == DiplomacyState.League ||
dipInfo.DiplomacyState == DiplomacyState.LeagueRupture)
return false;
//如果对方已经有一个来自我方的结盟请求
actionParams.TargetPlayerData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
if (dipInfo2.IsLeagueRequest) return false;
return true;
}
//Step #6 && #7 判断能否refuse / accept 结盟
if (_actionId.PlayerActionType == PlayerActionType.RefuseAlly || _actionId.PlayerActionType == PlayerActionType.AcceptAlly)
{
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
//如果刚解除结盟或者已经结盟 return
if (dipInfo.DiplomacyState == DiplomacyState.League ||
dipInfo.DiplomacyState == DiplomacyState.LeagueRupture)
return false;
//如果对方没有结盟请求
//actionParams.TargetPlayerData.DiplomacyData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
if (!dipInfo.IsLeagueRequest) return false;
return true;
}
//Step #8 判断能否break结盟
if (_actionId.PlayerActionType == PlayerActionType.BreakAlly)
{
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
if (dipInfo.IsTeammate) return false;
//如果没有结盟 return
if (dipInfo.DiplomacyState != DiplomacyState.League) return false;
//如果对方率先breakreturn
actionParams.TargetPlayerData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
if (dipInfo2.IsTeammate) return false;
if (dipInfo2.DiplomacyState == DiplomacyState.LeagueRupture) return false;
return true;
}
return false;
}
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
{
showType = ShowType.None;
//Step #1 鲁棒性判断
if (actionParams.PlayerData == null || !actionParams.PlayerData.IsSurvival) return false;
if (actionParams.MainObjectType != MainObjectType.Player) return false;
if (actionParams.TargetPlayerData == null || !actionParams.TargetPlayerData.IsSurvival ) return false;
if (!actionParams.PlayerData.MeetPlayers.Contains(actionParams.TargetPlayerData.Id)) return false;
if (IsDanegeldAction(_actionId.PlayerActionType))
return CheckShowDanegeld(actionParams, out showType);
//如果不是breakAlly默认返回团详细处理showType
if (_actionId.PlayerActionType != PlayerActionType.BreakAlly)
{
//Step #2 如果不具备科技 return
if (!actionParams.PlayerData.TechTree.CheckActionCan(_actionId))
{
showType = ShowType.Locked;
return true;
}
//Step #3 判断钱够不够
if (GetCost(actionParams) > actionParams.PlayerData.PlayerCoin)
{
showType = ShowType.Cost;
return true;
}
//Step #4 处理大使馆
if (_actionId.PlayerActionType == PlayerActionType.Embassy)
{
//如果对方没有原始首都,不可建立大使馆
actionParams.MapData.GetCapitalCityDataByPlayerId(actionParams.TargetPlayerData.Id,out var city);
if (!Main.CityLogic.CheckCradleCapital(actionParams.MapData, actionParams.TargetPlayerData, city))
{
showType = ShowType.Locked;
return true;
}
//如果已经在对方建立了大使馆,不可再次建立大使馆
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
if (dipInfo.IsEmbassy)
{
showType = ShowType.Done;
return true;
}
return true;
}
//Step #5 判断能否申请结盟
if (_actionId.PlayerActionType == PlayerActionType.OfferAlly)
{
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
//如果刚解除结盟或者已经结盟 return
if (dipInfo.DiplomacyState == DiplomacyState.League)
{
showType = ShowType.Done;
return true;
}
if(dipInfo.DiplomacyState == DiplomacyState.LeagueRupture)
{
showType = ShowType.Locked;
return true;
}
//如果对方已经有一个来自我方的结盟请求
actionParams.TargetPlayerData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
if (dipInfo2.IsLeagueRequest)
{
showType = ShowType.Send;
return true;
}
return true;
}
//Step #6 && #7 判断能否refuse / accept 结盟
if (_actionId.PlayerActionType == PlayerActionType.RefuseAlly || _actionId.PlayerActionType == PlayerActionType.AcceptAlly)
{
return true;
}
}
else
//Step #8 判断能否break结盟
{
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
if (dipInfo.IsTeammate) return false;
//如果没有结盟 return
if (dipInfo.DiplomacyState != DiplomacyState.League) return false;
//如果对方率先breakreturn
actionParams.TargetPlayerData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
if (dipInfo2.IsTeammate) return false;
if (dipInfo2.DiplomacyState == DiplomacyState.LeagueRupture) return false;
return true;
}
return false;
}
public override ActionShowState CheckShowState(CommonActionParams actionParams)
{
//Step #1 鲁棒性判断
if (actionParams.PlayerData == null) return ActionShowState.None;
if (actionParams.MainObjectType != MainObjectType.Player) return ActionShowState.None;
if (actionParams.TargetPlayerData == null) return ActionShowState.None;
MapData map = actionParams.MapData;
PlayerData player = actionParams.PlayerData;
PlayerData targetPlayer = actionParams.TargetPlayerData;
if (IsDanegeldAction(_actionId.PlayerActionType))
return CheckShowStateDanegeld(actionParams);
if (!map.GetCapitalCityDataByPlayerId(actionParams.TargetPlayerData.Id, out var city)) return ActionShowState.None;
if (!map.GetGridDataByCityId(city.Id, out var grid)) return ActionShowState.None;
actionParams.PlayerData.GetCountryDiplomacyInfo(actionParams.TargetPlayerData.Id, out var dipInfo);
actionParams.TargetPlayerData.GetCountryDiplomacyInfo(actionParams.PlayerData.Id,out var dipInfo2);
//Step #2 计算结盟按钮的情况
if (_actionId.PlayerActionType == PlayerActionType.OfferAlly)
{
//如果没有科技
if (!actionParams.PlayerData.TechTree.CheckActionCan(_actionId))
return ActionShowState.Unavailable;
//如果已经发送过,还没被回应
if(dipInfo2.IsLeagueRequest || dipInfo.DiplomacyState == DiplomacyState.LeagueRupture) return ActionShowState.Unavailable;
//如果已经结盟
if (dipInfo.DiplomacyState == DiplomacyState.League ) return ActionShowState.Finished;
//其他情况一律是available
return ActionShowState.Available;
}
//Step #3 计算breakAlly按钮的情况
if (_actionId.PlayerActionType == PlayerActionType.BreakAlly)
{
if (dipInfo.IsTeammate || dipInfo2.IsTeammate) return ActionShowState.Unavailable;
return ActionShowState.Available;
}
//Step #4 计算embassy按钮的情况
if (_actionId.PlayerActionType == PlayerActionType.Embassy)
{
//如果没有科技
if (!actionParams.PlayerData.TechTree.CheckActionCan(_actionId))
return ActionShowState.Unavailable;
//如果对方已经丢失原始首都
if (!map.GetCapitalCityDataByPlayerId(targetPlayer.Id, out var targetCapital)) return ActionShowState.None;
if (!Main.CityLogic.CheckCradleCapital(map, targetPlayer, targetCapital)) return ActionShowState.Unavailable;
//如果钱不够
if (player.PlayerCoin < GetCost(actionParams)) return ActionShowState.Expensive;
//如果已经建立了大使馆
if (dipInfo.IsEmbassy) return ActionShowState.Finished;
//其他情况一律是available
return ActionShowState.Available;
}
return ActionShowState.None;
}
public static bool IsDanegeldAction(PlayerActionType playerActionType)
{
return playerActionType == PlayerActionType.DanegeldDemand
|| playerActionType == PlayerActionType.DanegeldPay
|| playerActionType == PlayerActionType.DanegeldReject;
}
public static void GetPendingDanegeldDemanders(MapData map, PlayerData targetPlayer, List<PlayerData> result)
{
result?.Clear();
if (map?.PlayerMap?.PlayerDataList == null || targetPlayer == null || result == null) return;
foreach (var demander in map.PlayerMap.PlayerDataList)
{
if (demander == null || demander.Id == targetPlayer.Id) continue;
if (!demander.IsSurvival) continue;
if (!TryGetDanegeldRelation(demander, targetPlayer, out var info)) continue;
if (info.DanegeldState == DanegeldState.Demanding) result.Add(demander);
}
result.Sort((a, b) => a.Id.CompareTo(b.Id));
}
public static bool RejectPendingDanegeldDemandsInTurnEnd(MapData map, PlayerData targetPlayer)
{
if (map == null || targetPlayer == null) return false;
var demanders = new List<PlayerData>();
GetPendingDanegeldDemanders(map, targetPlayer, demanders);
var rejected = false;
foreach (var demander in demanders)
{
var actionId = new CommonActionId
{
ActionType = CommonActionType.PlayerAction,
PlayerActionType = PlayerActionType.DanegeldReject
};
var action = ActionLogicFactory.GetActionLogic(actionId);
var param = new CommonActionParams(mapData: map, playerData: targetPlayer,
targetPlayer: demander, mainObjectType: MainObjectType.Player);
param.RefreshParams();
if (action?.ExecuteWithoutFullActionPeriod(param) == true) rejected = true;
}
return rejected;
}
public static void ClearExpiredDanegeldRefusals(MapData map, PlayerData targetPlayer)
{
if (map?.PlayerMap?.PlayerDataList == null || targetPlayer == null) return;
foreach (var demander in map.PlayerMap.PlayerDataList)
{
if (demander == null || demander.Id == targetPlayer.Id) continue;
if (!TryGetDanegeldRelation(demander, targetPlayer, out var info)) continue;
if (info.DanegeldState != DanegeldState.Refused) continue;
if (targetPlayer.Turn <= info.DanegeldRefusedTurn) continue;
info.DanegeldState = DanegeldState.Clear;
info.DanegeldRefusedTurn = 0;
}
}
public static int GetDanegeldRaidMultiplier(PlayerData raider, PlayerData targetPlayer)
{
if (raider == null || targetPlayer == null) return 1;
if (!TryGetDanegeldRelation(raider, targetPlayer, out var info)) return 1;
return info.DanegeldState == DanegeldState.Refused ? 2 : 1;
}
private bool ExecuteDanegeld(CommonActionParams actionParams, PlayerData player, PlayerData targetPlayer)
{
if (_actionId.PlayerActionType == PlayerActionType.DanegeldDemand)
{
if (!TryGetDanegeldRelation(player, targetPlayer, out var info)) return false;
player.SpendCoin(GetDanegeldDemandCost(actionParams));
info.DanegeldState = DanegeldState.Demanding;
info.LastDanegeldDemandTurn = player.Turn;
info.DanegeldRefusedTurn = 0;
if(!targetPlayer.MeetPlayers.Contains(player.Id))
Main.PlayerLogic.AddMeetPlayer(actionParams.MapData,targetPlayer,player);
if (actionParams.MapData == Main.MapData && player.Id == Main.MapData.PlayerMap.SelfPlayerId)
{
EventManager.Publish(new ShowUINotifyCommon(){UINotifyCommonType = UINotifyCommonType.HakureiDanegeldDemandStarted});
EventManager.Publish(new HideUIInfoDiplomacy());
}
return true;
}
if (!TryGetDanegeldRelation(targetPlayer, player, out var demanderToPlayer)) return false;
if (_actionId.PlayerActionType == PlayerActionType.DanegeldPay)
{
var payAmount = GetDanegeldActualPaymentAmount(actionParams);
if (payAmount > 0)
{
player.SpendCoin(payAmount);
targetPlayer.AddCoin(payAmount);
}
ReimuNorwaySkillUtil.EnqueueDanegeldPaymentNotice(actionParams.MapData, player, targetPlayer.Id,
payAmount, targetPlayer.Id);
demanderToPlayer.DanegeldState = DanegeldState.Clear;
demanderToPlayer.DanegeldRefusedTurn = 0;
return true;
}
if (_actionId.PlayerActionType == PlayerActionType.DanegeldReject)
{
var demandedAmount = GetDanegeldPaymentAmount(actionParams);
demanderToPlayer.DanegeldState = DanegeldState.Refused;
demanderToPlayer.DanegeldRefusedTurn = player.Turn;
ReimuNorwaySkillUtil.EnqueueDanegeldRejectedNotice(actionParams.MapData, player, targetPlayer.Id,
demandedAmount, targetPlayer.Id);
return true;
}
return false;
}
private bool CheckCanDanegeld(CommonActionParams actionParams)
{
var player = actionParams.PlayerData;
var targetPlayer = actionParams.TargetPlayerData;
if (player == null || targetPlayer == null || player.Id == targetPlayer.Id) return false;
if (_actionId.PlayerActionType == PlayerActionType.DanegeldDemand)
{
if (!player.TechTree.CheckActionCan(_actionId)) return false;
if (player.PlayerCoin < GetDanegeldDemandCost(actionParams)) return false;
if (!TryGetDanegeldRelation(player, targetPlayer, out var info)) return false;
if (!TryGetDanegeldRelation(targetPlayer, player, out var targetInfo)) return false;
if (info.IsTeammate || targetInfo.IsTeammate) return false;
if (actionParams.MapData.SameUnion(player.Id, targetPlayer.Id)) return false;
if (info.DiplomacyState == DiplomacyState.League || targetInfo.DiplomacyState == DiplomacyState.League)
return false;
if (CheckDanegeldDemandInCooldown(player, info)) return false;
return info.DanegeldState == DanegeldState.Clear;
}
if (!TryGetDanegeldRelation(targetPlayer, player, out var demanderToPlayer)) return false;
return demanderToPlayer.DanegeldState == DanegeldState.Demanding;
}
private bool CheckShowDanegeld(CommonActionParams actionParams, out ShowType showType)
{
showType = ShowType.None;
if (_actionId.PlayerActionType != PlayerActionType.DanegeldDemand)
return CheckCanDanegeld(actionParams);
if (!Table.Instance.PlayerDataAssets.CheckActionInTechPool(_actionId, actionParams.PlayerData))
return false;
if (!actionParams.PlayerData.TechTree.CheckActionCan(_actionId))
{
showType = ShowType.Locked;
return true;
}
if (!TryGetDanegeldRelation(actionParams.PlayerData, actionParams.TargetPlayerData, out var info))
return false;
if (!TryGetDanegeldRelation(actionParams.TargetPlayerData, actionParams.PlayerData, out var targetInfo))
return false;
if (info.IsTeammate || targetInfo.IsTeammate || actionParams.MapData.SameUnion(actionParams.PlayerData.Id, actionParams.TargetPlayerData.Id)
|| info.DiplomacyState == DiplomacyState.League || targetInfo.DiplomacyState == DiplomacyState.League)
{
showType = ShowType.Locked;
return true;
}
if (info.DanegeldState == DanegeldState.Demanding)
{
showType = ShowType.Send;
return true;
}
if (info.DanegeldState == DanegeldState.Refused)
{
showType = ShowType.Locked;
return true;
}
if (CheckDanegeldDemandInCooldown(actionParams.PlayerData, info))
{
showType = ShowType.Cold;
return true;
}
if (actionParams.PlayerData.PlayerCoin < GetDanegeldDemandCost(actionParams))
{
showType = ShowType.Cost;
return true;
}
return true;
}
private ActionShowState CheckShowStateDanegeld(CommonActionParams actionParams)
{
if (_actionId.PlayerActionType != PlayerActionType.DanegeldDemand)
return CheckCanDanegeld(actionParams) ? ActionShowState.Available : ActionShowState.None;
if (!Table.Instance.PlayerDataAssets.CheckActionInTechPool(_actionId, actionParams.PlayerData))
return ActionShowState.None;
if (!actionParams.PlayerData.TechTree.CheckActionCan(_actionId))
return ActionShowState.Unavailable;
if (!TryGetDanegeldRelation(actionParams.PlayerData, actionParams.TargetPlayerData, out var info))
return ActionShowState.None;
if (!TryGetDanegeldRelation(actionParams.TargetPlayerData, actionParams.PlayerData, out var targetInfo))
return ActionShowState.None;
if (info.IsTeammate || targetInfo.IsTeammate || actionParams.MapData.SameUnion(actionParams.PlayerData.Id, actionParams.TargetPlayerData.Id)
|| info.DiplomacyState == DiplomacyState.League || targetInfo.DiplomacyState == DiplomacyState.League)
return ActionShowState.Unavailable;
if (info.DanegeldState != DanegeldState.Clear) return ActionShowState.Unavailable;
if (CheckDanegeldDemandInCooldown(actionParams.PlayerData, info)) return ActionShowState.Unavailable;
if (actionParams.PlayerData.PlayerCoin < GetDanegeldDemandCost(actionParams))
return ActionShowState.Expensive;
return ActionShowState.Available;
}
public override int GetCost(CommonActionParams actionParams)
{
return _actionId.PlayerActionType == PlayerActionType.DanegeldDemand
? GetDanegeldDemandCost(actionParams)
: base.GetCost(actionParams);
}
public static int GetDanegeldDemandCost(CommonActionParams actionParams)
{
return GetDanegeldDemandCost(actionParams?.MapData, actionParams?.PlayerData);
}
public static int GetDanegeldDemandCost(MapData map, PlayerData player)
{
if (map == null || player == null) return 0;
return Mathf.Min(map.GetCityCount(player.Id) * 3, 15);
}
public static int GetDanegeldPaymentAmount(CommonActionParams actionParams)
{
return GetDanegeldPaymentAmount(actionParams?.MapData, actionParams?.PlayerData);
}
public static int GetDanegeldPaymentAmount(MapData map, PlayerData player)
{
if (map == null || player == null) return 0;
var coinPerTurn = Main.PlayerLogic.GetPlayerCoinPerTurn(map, player.Id);
if (coinPerTurn <= 0) return 0;
return (coinPerTurn + 4) / 5;
}
public static int GetDanegeldActualPaymentAmount(CommonActionParams actionParams)
{
return GetDanegeldActualPaymentAmount(actionParams?.MapData, actionParams?.PlayerData);
}
public static int GetDanegeldActualPaymentAmount(MapData map, PlayerData player)
{
if (map == null || player == null) return 0;
return Mathf.Min(GetDanegeldPaymentAmount(map, player), Mathf.Max(0, player.PlayerCoin));
}
private static bool CheckDanegeldDemandInCooldown(PlayerData demander, CountryDiplomacyInfo info)
{
if (demander == null || info == null || info.LastDanegeldDemandTurn == 0) return false;
return demander.Turn < info.LastDanegeldDemandTurn + 2;
}
private static bool TryGetDanegeldRelation(PlayerData demander, PlayerData targetPlayer, out CountryDiplomacyInfo info)
{
info = null;
if (demander == null || targetPlayer == null) return false;
return demander.GetCountryDiplomacyInfo(targetPlayer.Id, out info) && info != null;
}
}
//如果有空的槽位那么将空的槽位设置为选择的特定hero。
//[actionId参数]只要一个giantType即可
//[param参数]只要player即可
public class PlayerActionSelectHero : PlayerActionAction
{
public PlayerActionSelectHero(CommonActionId id) : base(id) { }
protected override bool Execute(CommonActionParams actionParams)
{
if (!CheckCan(actionParams)) return false;
var player = actionParams.PlayerData;
var heroD = player.PlayerHeroData;
return heroD?.AddHero(actionParams.MapData,actionParams.PlayerData,_actionId.GiantType)??false;
}
public override bool CheckCan(CommonActionParams actionParams)
{
//Step #1 基础鲁棒性检查
if (!PlayerActionCheckBaseInfo(actionParams)) return false;
var player = actionParams.PlayerData;
var heroD = player.PlayerHeroData;
//Step #2 检查英雄类型属于当前玩家阵营
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType.Giant, _actionId.GiantType,
1, out var info)) return false;
if (info.GiantEmpire != actionParams.PlayerData.Empire) return false;
if (!ContentGate.CanUseHeroForPlayer(player, _actionId.GiantType))
{
#if UNITY_EDITOR
if (!Logic.AI.AIDirectorBatchRuntime.SkipPresentationWait)
LogSystem.LogWarning($"Blocked unavailable hero select: player={player.Id}, giant={_actionId.GiantType}");
#else
LogSystem.LogWarning($"Blocked unavailable hero select: player={player.Id}, giant={_actionId.GiantType}");
#endif
return false;
}
//Step #3 检查当前玩家的HeroData是否已经出了英雄且拥有空的槽位
if (heroD.HeroCount >= heroD.MaxHeroCount) return false;
if (heroD.HasHero(_actionId.GiantType)) return false;
return true;
}
}
public class PlayerActionTreasureReward : PlayerActionAction
{
public PlayerActionTreasureReward(CommonActionId id) : base(id) { }
protected override bool Execute(CommonActionParams actionParams)
{
if (!CheckCan(actionParams)) return false;
var player = actionParams.PlayerData;
var groupId = actionParams.GridId;
if (!player.TryGetTreasureActionOptionGroup(groupId, out var group)) return false;
if (!group.TryGetOption(_actionId, out var option)) return false;
var ret = ExecuteTreasureReward(actionParams, group, option);
if (!ret) return false;
player.RemoveTreasureActionOptionGroup(groupId);
return true;
}
public override bool CheckCan(CommonActionParams actionParams)
{
if (!PlayerActionCheckBaseInfo(actionParams)) return false;
if (actionParams.MainObjectType != MainObjectType.Player) return false;
if (!IsTreasureRewardAction(_actionId.PlayerActionType)) return false;
if (actionParams.GridId == 0) return false;
if (!actionParams.PlayerData.TryGetTreasureActionOptionGroup(actionParams.GridId, out var group)) return false;
return group.TryGetOption(_actionId, out _);
}
public override bool CheckShow(CommonActionParams actionParams, out ShowType showType)
{
showType = ShowType.None;
return CheckCan(actionParams);
}
private static bool IsTreasureRewardAction(PlayerActionType playerActionType)
{
return playerActionType == PlayerActionType.TreasureGainCoin
|| playerActionType == PlayerActionType.TreasureGainUnit
|| playerActionType == PlayerActionType.TreasureGainTech
|| playerActionType == PlayerActionType.TreasureGainCulture
|| playerActionType == PlayerActionType.TreasureGainCityExp;
}
private bool ExecuteTreasureReward(CommonActionParams actionParams, ActionOptionGroup group, ActionOption option)
{
if (!actionParams.MapData.GridMap.GetGridDataByGid(group.SourceGridId, out var sourceGrid)) return false;
var player = actionParams.PlayerData;
if (option.RewardType == TreasureActionRewardType.Coin)
{
player.AddCoin(option.Value, Table.Instance.GridToWorld(sourceGrid));
return true;
}
if (option.RewardType == TreasureActionRewardType.Unit)
{
if (!actionParams.MapData.GetCapitalCityDataByPlayerId(player.Id, out var capital)) return false;
if (!actionParams.MapData.GetGridDataByCityId(capital.Id, out var capitalGrid)) return false;
if (actionParams.MapData.UnitMap.GetUnitDataByUnitId(group.SourceUnitId, out var sourceUnit) &&
actionParams.MapData.GetGridDataByUnitId(sourceUnit.Id, out var sourceUnitGrid) &&
sourceUnitGrid.Id == sourceGrid.Id)
Main.UnitLogic.PassiveMoveAway(actionParams.MapData, sourceUnit);
if (!actionParams.MapData.AddUnitData(sourceGrid.Id, capital.Id, option.UnitFullType, out var unit) &&
!actionParams.MapData.AddUnitData(capitalGrid.Id, capital.Id, option.UnitFullType, out unit))
return false;
if (option.UnitFullType.UnitType == UnitType.RammerShip)
unit.AddSkill_Legacy(SkillType.OFFICER, actionParams.MapData, true, -1, false, -1, false, SpecialAddSkillType.Normal, 0);
unit.SetOfficer();
unit.Health = unit.GetMaxHealth();
unit.Renderer(actionParams.MapData)?.InstantUpdateUnit(true);
capital.SetCityRenderer(actionParams.MapData);
return true;
}
if (option.RewardType == TreasureActionRewardType.Tech)
{
if (option.TechType == TechType.None) return false;
Main.PlayerLogic.ResearchTech(actionParams.MapData, player, option.TechType, 0);
if (player.IsSelfPlayer())
EventManager.Publish(new ShowUINotifyCommon(){UINotifyCommonType = UINotifyCommonType.ExamineTech});
return true;
}
if (option.RewardType == TreasureActionRewardType.Culture)
{
player.AddCulturePoint(option.Value);
if (player.IsSelfPlayer())
EventManager.Publish(new ShowUINotifyCommon(){UINotifyCommonType = UINotifyCommonType.ExamineCulture});
return true;
}
if (option.RewardType == TreasureActionRewardType.CityExp)
{
if (!actionParams.MapData.GetCapitalCityDataByPlayerId(player.Id, out var capital)) return false;
Main.CityLogic.GridGiveCityExp_LogicView(actionParams.MapData, player, capital.Grid(actionParams.MapData), capital, option.Value);
if (player.IsSelfPlayer())
EventManager.Publish(new ShowUINotifyCommon(){UINotifyCommonType = UINotifyCommonType.ExamineCityExp});
return true;
}
return false;
}
}
//立即完成一个英雄的升级任务
//[actionId参数]只要一个giantType即可
//[param参数]只要player即可
public class PlayerActionFinishHeroTask : PlayerActionAction
{
public PlayerActionFinishHeroTask(CommonActionId id) : base(id) { }
protected override bool Execute(CommonActionParams actionParams)
{
var player = actionParams.PlayerData;
var heroD = player.PlayerHeroData;
player.GetHeroCostDiscount(actionParams.MapData, out var discount);
var cost = heroD.GetHeroFinishTaskCost(_actionId.GiantType, discount);
var can = heroD.ForceFinishHeroTask(_actionId.GiantType);
if (can)
{
if (actionParams.MapData.CheckIsRealPlayer(actionParams.PlayerData.Id))
{
actionParams.PlayerData.AddCulturePoint(-cost);
}
}
return can;
}
public override bool CheckCan(CommonActionParams actionParams)
{
//Step #1 基础鲁棒性检查
if (!PlayerActionCheckBaseInfo(actionParams)) return false;
var player = actionParams.PlayerData;
var heroD = player.PlayerHeroData;
//Step #2 检查英雄类型属于当前玩家阵营
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType.Giant, _actionId.GiantType,
1, out var info)) return false;
if (info.GiantEmpire != actionParams.PlayerData.Empire) return false;
if (!ContentGate.CanUseHeroForPlayer(player, _actionId.GiantType))
{
LogSystem.LogWarning($"Blocked unavailable hero task finish: player={player.Id}, giant={_actionId.GiantType}");
return false;
}
//Step #3 检查player是否拥有该英雄
if (!heroD.HasHero(_actionId.GiantType)) return false;
//Step #4 如果是玩家,检查文化值是否够
if (actionParams.PlayerData == actionParams.MapData.PlayerMap.SelfPlayerData)
{
player.GetHeroCostDiscount(actionParams.MapData, out var discount);
var cost = heroD.GetHeroFinishTaskCost(_actionId.GiantType, discount);
if (cost > actionParams.PlayerData.PlayerCultureInfo.PlayerCulture)
return false;
}
return true;
}
}
}