2026-04-24 18:22:21 +08:00

605 lines
24 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.

using System;
using System.Collections.Generic;
using Logic.CrashSight;
using Logic.Pool;
using MongoDB.Driver.Core.Misc;
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;
namespace Logic
{
public class CityLogic : ICityLogic
{
bool needExplorer = false;
int explorerStepMax = 10;
int explorerStep = 0;
Vector2Int explorerNowPos = new Vector2Int();
CityData explorerCityData;
private List<GridData> _aroundBuf;
public void GenerateTribe(MapData mapData, Vector2Int tpos, int cid)
{
}
public void TribeToCity(MapData mapData, PlayerData pd, CityData cd, UnitData ud)
{
}
public void CityRemoveUnitFromList(MapData mapData, int uid, int cid)
{
}
public void CityNewUnit(MapData mapData, int cid, UnitType unitType)
{
}
public void CityExploit(MapData mapData, int cid, Vector2Int tpos, string actionName)
{
}
//-------------------------------------- 构造方法 --------------------------------------//
public CityLogic()
{
}
public void Update()
{
}
//-------------------------------------- 城市逻辑相关方法 --------------------------------------//
// 更新城市等级
//用来增加城市经验,返回值表示本次升了几级,这里不能处理视觉
private int CityUpdateExp_LogicOnly(MapData mapData, CityData cityData, int exp)
{
cityData.LevelExp += exp;
//cityData.CityInfoRenderer(mapData)?.SetUpdateCityInfo(true);
return CityUpdateLevel(mapData, cityData);
}
//返回是否升级成功
private int CityUpdateLevel(MapData mapData, CityData cityData)
{
int tmp = cityData.Level;
if (!cityData.TryUpgradeCity()) return 0;
//cityData.GridRenderer(mapData)?.SetUpdateGrid(true);
//cityData.CityInfoRenderer(mapData)?.SetUpdateCityInfo(true);
//if (!mapData.GetGridDataByCityId(cityData.Id, out var gridData)) return 0;
//gridData.Renderer(mapData)?.SetUpdateCityBuilding(true);
var levelUp = cityData.Level - tmp;
cityData.CityLevelUpPoint += levelUp;
//Step #2 MOMENT
if (mapData == Main.MapData && cityData.Player(mapData) == Main.MapData.PlayerMap.SelfPlayerData &&
cityData.Level > 5)
{
EventManager.Publish(new ShowUINotifyMoment()
{
MomentSubType = MomentSubType.ExploitBigCity,
Empire = Main.MapData.PlayerMap.SelfPlayerData.Empire
});
}
return levelUp;
}
//城市升级的唯一通用方法 处理了logic和view
public bool GridGiveCityExp_LogicView(MapData map,PlayerData player,GridData grid,CityData city,int cityExp)
{
if (map == null || player == null || grid == null || city == null) return false;
if (cityExp == 0) return false;
int expPerStep = cityExp > 0 ? 1 : -1;
var stepCount = Mathf.Abs(cityExp);
//如果是真人玩家且exp>0 ,出发一组序列动画
if (player.IsSelfPlayer())
{
//接下来需要1exp 1exp的增加来处理城市升级否则可能出现连续升级城市选择坏掉的情况
for (int i = 0; i < stepCount; i++)
{
//Step #1逻辑层处理城市获得cityExp
int oldLevel = city.Level;
int oldLevelExp = city.LevelExp;
int levelup = Main.CityLogic.CityUpdateExp_LogicOnly(map,city,expPerStep);
//Step #2 表现层处理城市获得cityExp的动画
if (map.IsCurrentShowMap())
{
if (player.IsSelfPlayer())
{
PresentationManager.EnqueueTaskGroupCityExp(expPerStep,oldLevel,oldLevelExp,map,grid,city);
//部分grid还需要增加faith
/*if(grid.Resource == ResourceType.Wonder)
player.AddFaith(300, grid);*/
}
}
}
}
//否则就是AI操作直接更新grid的雾效然后更新城市的cityInfo和cityBuilding
else
{
int levelup = Main.CityLogic.CityUpdateExp_LogicOnly(map,city,cityExp);
if (levelup > 0)
{
if (map.IsCurrentShowMap())
{
//城市升级则播放雾效
city.Grid(Main.MapData)?.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
city.SetCityRenderer(map);
}
}
}
return true;
}
public bool UpdateGrid_ViewOnly(MapData map,GridData grid)
{
if (grid.InMainSight())
{
//actionParams.GridData.Renderer(actionParams.MapData)?.PlayVFX(new GridVFXParams(GridVFXType.Fog));
PresentationManager.EnqueueGridUpdate(map,grid,GridUpdateType.BuildingUpdate);
//var dataGridUpdate = FragmentDataFactory.Create(FragmentType.GridUpdate,GridUpdateType.BuildingUpdate, grid.Renderer(map));
//PresentationManager.EnqueueTask(new FragmentSequencerTask(FragmentFactory.Create(FragmentType.GridUpdate,dataGridUpdate)));
//这里还可以处理如果有单位再上面的情况
if (grid.MainSelfPlayerVisibleUnit(out var unit))
unit.Renderer(map)?.InstantUpdateUnit(true);
}
return true;
}
public bool CityExpDecrease()
{
return true;
}
// 将一个城市设置为首都
public void SetCapital(MapData mapData, uint cid)
{
if (!mapData.CityMap.GetCityById(cid, out CityData cityData)) return;
cityData.SetCapital(true,mapData);
}
//更新grid的buildinglevel并且顺带会更新城市的exp用于例如种田后旁边的谷仓也升级 连带的城市升级行动序列
public bool UpdateGridBuildingData_LogicView(MapData mapData, GridData gridData)
{
//Step #1 计算最新的buildinglevel
CalcGridBuildingLevel(mapData, gridData, gridData.Resource, out var level);
if (!mapData.GetPlayerDataByTerritoryGridId(gridData.Id, out var player)) return false;
if (!Table.Instance.GridAndResourceDataAssets.GetResourceInfo(gridData.Resource, out var resourceInfo))
{
LogSystem.LogError($"CityLogic UpdateGridBuildingData can't find data asset of resource{gridData.Resource}");
return false;
}
//Step #2 计算这次的差量变化
int levelDelta = level - gridData.buildingLevel;
int delta = (level - gridData.buildingLevel ) * resourceInfo.CityExpPerLevel;
//tree保护区也会给经验要特判 TODO 之后要改
if (gridData.Resource == ResourceType.Preserve && gridData.Vegetation == Vegetation.Trees)
delta = levelDelta * 1;
gridData.buildingLevel = level;
if (!mapData.GetCityDataByTerritoryGid(gridData.Id, out var cityData)) return false;
//Step #3 处理给城市经验,逻辑层&视觉层一同处理
if (delta != 0) GridGiveCityExp_LogicView(mapData,player,gridData,cityData,delta);
//Step #4 如果仅仅是建筑变化,没有给城市经验,逻辑&视觉处理
if (delta != 0 || levelDelta != 0)
{
var data = FragmentDataFactory.Create(FragmentType.GridUpdate, GridUpdateType.FogDisappear, gridData.Renderer(mapData),Table.Instance.AnimDataAssets.GridUpdateAnimTime);
PresentationManager.EnqueueTask(new FragmentSequencerTask(FragmentFactory.Create(FragmentType.GridUpdate, data)));
}
return true;
}
//获得一个city当前的科技点
public int GetCityTechPointPerTurn(MapData mapData, CityData cityData)
{
int ret = 0;
if (GetBuildingLevelByCity(mapData, cityData, ResourceType.Academy, out var academyLevel))
ret = academyLevel;
//增加保护区的科技
foreach (var gid in cityData.Territory.TerritoryArea)
{
if (!mapData.GridMap.GetGridDataByGid(gid, out var targetGrid)) continue;
if (targetGrid.Resource != ResourceType.Preserve) continue;
if (targetGrid.Terrain != TerrainType.DeepSea &&
targetGrid.Feature != TerrainFeature.Mountain) continue;
ret += targetGrid.buildingLevel;
}
return ret;
}
//获得一个city当前的文化点
public int GetCityCulturePointPerTurn(MapData mapData, CityData cityData)
{
int ret = 0;
// 获取城市所属玩家
if (!mapData.GetPlayerDataByCityId(cityData.Id, out var cityPlayer))
return ret;
// 判断是否是当前占领者的原始首都
bool isMyCradleCity = cityPlayer.CradleCityId == cityData.Id;
// 判断是否是其他玩家的原始首都
bool isOtherPlayerCradleCity = false;
foreach (var player in mapData.PlayerMap.PlayerDataList)
{
if (player.Id == cityPlayer.Id) continue; // 跳过当前玩家
if (player.CradleCityId == cityData.Id)
{
isOtherPlayerCradleCity = true;
break;
}
}
if (isMyCradleCity)
{
// 是当前占领者的原始首都 +3
ret += 3;
}
else if (isOtherPlayerCradleCity)
{
// 是其他玩家的原始首都(被占领的)+1
ret += 1;
}
else if (cityData.IsCapital)
{
// 不是任何玩家的原始首都,但是是首都 +1
ret += 1;
}
// 增加公园数量对应的文化点
ret += cityData.ParkCount;
// 遍历领土统计Temple类建筑数量
foreach (var gid in cityData.Territory.TerritoryArea)
{
if (!mapData.GridMap.GetGridDataByGid(gid, out var grid)) continue;
// 检查是否为Temple类建筑
if (IsTempleResource(grid.Resource))
ret += 1;
}
return ret;
}
// 判断是否为Temple类建筑
private bool IsTempleResource(ResourceType resource)
{
return resource == ResourceType.Temple
|| resource == ResourceType.ForestTemple
|| resource == ResourceType.WaterTemple
|| resource == ResourceType.MountainTemple
|| resource == ResourceType.KingTemple;
}
//获得一个city当前的回合钱
public int GetCityCoinPerTurn(MapData mapData, CityData cityData)
{
//如果城市亏exp要减少每回合的给钱数
int mins = 0;
if (cityData.LevelExp < 0)
mins = cityData.LevelExp;
int ret = 0;
ret = cityData.Level - 1 + cityData.ParkCount + (mapData.GetPlayerDataByCityId(cityData.Id,out var player) && Main.CityLogic.CheckCradleCapital(mapData,player,cityData) ? 1 : 0) + (cityData.Workshop ? 1 : 0) + mins;
//增加集市的金钱
if (GetBuildingLevelByCity(mapData, cityData, ResourceType.Market, out var marketLevel))
ret += marketLevel;
//增加保护区的金钱
foreach (var gid in cityData.Territory.TerritoryArea)
{
if (!mapData.GridMap.GetGridDataByGid(gid, out var targetGrid)) continue;
if (targetGrid.Resource != ResourceType.Preserve) continue;
if (targetGrid.Terrain != TerrainType.ShallowSea) continue;
ret += targetGrid.buildingLevel;
}
//增加kaguyaYard的金钱
if (GetBuildingLevelByCity(mapData, cityData, ResourceType.KaguyaFrenchYard, out var kaguyaYardLevel))
ret += kaguyaYardLevel;
if (ret < 0)
ret = 0;
return ret;
}
//获得一个city当前的某种building的level
public bool GetBuildingLevelByCity(MapData mapData, CityData cityData, ResourceType buildingType, out int level)
{
foreach (var gridId in cityData.Territory.TerritoryArea)
{
if (!mapData.GridMap.GetGridDataByGid(gridId, out var gridData))
continue;
if (gridData.Resource == buildingType)
{
level = gridData.buildingLevel;
return true;
}
}
level = 0;
return false;
}
// 城市升级领土
public void CityLevelUpActionExpand(MapData mapData, CityData cityData)
{
if (!mapData.GetGridDataByCityId(cityData.Id, out var gridData)) return;
if (!mapData.GetPlayerDataByCityId(cityData.Id, out var playerData)) return;
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(2, 2, gridData, _aroundBuf);
Main.PlayerLogic.UpdateSight_LogicView(mapData,playerData,mapData.GridMap.GetAroundGridIdList(2,gridData));
using var pooledNewTerritoryArea = THCollectionPool.GetListHandle<uint>(out var newTerritoryArea);
foreach (var aroundGrid in _aroundBuf)
{
if (mapData.GetPlayerDataByTerritoryGridId(aroundGrid.Id, out var playerData1)) continue;
newTerritoryArea.Add(aroundGrid.Id);
var t = new GridVFXParams(GridVFXType.Flag);
t.CivId = playerData.PlayerCivId;
aroundGrid.Renderer(mapData)?.PlayVFXInSight(t);
}
cityData.Territory.UpdateTerritory(newTerritoryArea);
var playerTerritoryGridIdSet = mapData.GetPlayerTerritoryGridIdSet(playerData.Id);
foreach (var gridId in playerTerritoryGridIdSet)
{
if (!mapData.GridMap.GetGridDataByGid(gridId, out var gridData1))
continue;
gridData1.Renderer(mapData)?.InstantUpdateGrid(true);
}
}
// 查询如果gridData上有buildingType,那么它的level会是多少,bool返回他能否建设在这个位置
//两种用法第一种是还没有建设但是假设这里建设了第二种是已经建设了需要更新计算最新的level
public bool CalcGridBuildingLevel(MapData mapData, GridData gridData,ResourceType buildingType,out int level)
{
if(!Table.Instance.GridAndResourceDataAssets.GetResourceInfo(buildingType,out var resourceInfo))
{
LogSystem.LogError($"CityLogic CalcGridBuildingLevel cant get resourceInfo dataasset of {buildingType}");
level = 0;
return false;
}
if (!resourceInfo.HasLevel)
{
level = 0;
return false;
}
//获得周围一圈的grid必须是我方
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1,1,gridData, _aroundBuf);
level = 0;
bool ret = false;
//Preserve单独处理
if (buildingType == ResourceType.Preserve)
{
//先判断能不能建造保护区
if (gridData.Vegetation == Vegetation.Trees &&
gridData.Resource is ResourceType.Animal or ResourceType.None) ret = true;
if (gridData.Feature == TerrainFeature.Mountain &&
gridData.Resource is ResourceType.None or ResourceType.Metal) ret = true;
if (gridData.Terrain != TerrainType.Land &&
gridData.Resource is ResourceType.None or ResourceType.Fish or ResourceType.Starfish) ret = true;
level = 1;
//判断4个方向是否有成为国家公园的可能只要可能那就是level=2即国家公园
for(int x = -1;x <= 1;x += 2)
for(int y = -1;y <= 1; y += 2)
if (CheckIsNationPark(mapData,gridData, x, y))
{
level = 2;
return ret;
}
return ret;
}
//其他建筑处理
foreach (var grid in _aroundBuf)
{
//如果就是中间这个格子,跳过
//if (grid == gridData) continue;
//如果无主或者不是和gridData相同的领土直接continue
if (!mapData.GetPlayerDataByTerritoryGridId(grid.Id, out var playerData) ||
playerData != gridData.Player(mapData))
continue;
//鲁棒性 + 获得该grid的cityData
if (!mapData.GetCityDataByTerritoryGid(grid.Id, out var targetCityData))
continue;
//处理market的情况
if (buildingType == ResourceType.Market)
{
if (grid.Resource is ResourceType.Forge or ResourceType.Windmill or ResourceType.Sawmill or ResourceType.Preserve or ResourceType.EgyptianIrrigation)
{
level += grid.buildingLevel;
ret = true;
}
if (level > 8)
level = 8;
}
//处理windmill forge话sawmill的情况
if ((grid.Resource == ResourceType.Farm && buildingType == ResourceType.Windmill)
|| (grid.Resource == ResourceType.Mine && buildingType == ResourceType.Forge)
|| (grid.Resource == ResourceType.LumberHut && buildingType == ResourceType.Sawmill))
{
level++;
ret = true;
}
//处理academy
if (buildingType == ResourceType.Academy)
{
if (grid.Feature == TerrainFeature.Mountain)
{
level++;
ret = true;
}
if (grid.Resource == ResourceType.Preserve)
{
level+= grid.buildingLevel;
ret = true;
}
if (grid.Resource == ResourceType.Forge)
{
level+= grid.buildingLevel;
ret = true;
}
}
//处理kaguyafrench yard
if (buildingType == ResourceType.KaguyaFrenchYard)
{
if (grid.Resource == ResourceType.Animal)
{
level++;
ret = true;
}
}
//处理egyptianirrigation
if (buildingType == ResourceType.EgyptianIrrigation)
{
if (grid.Resource == ResourceType.Farm)
{
level++;
ret = true;
}
}
}
// ===== 地脉加成(仅 Indian 阵营生效) =====
if (mapData.GetPlayerDataByTerritoryGridId(gridData.Id, out var leyLinePlayer)
&& leyLinePlayer.CivEnum == CivEnum.Indian)
{
if (gridData.HasSpType(GridSpType.LeyLine))
{
int bonus = (buildingType == ResourceType.Forge) ? 2 : 3;
level += bonus;
ret = true;
}
else
{
foreach (var grid in _aroundBuf)
{
if (grid == gridData) continue;
if (grid.HasSpType(GridSpType.LeyLine))
{
level += 1;
ret = true;
break;
}
}
}
}
// ====================
if (level > resourceInfo.MaxLevel)
level = resourceInfo.MaxLevel;
return ret;
}
//判断是否国家公园(x,y->x+dx,y+dy)
public bool CheckIsNationPark(MapData mapData, GridData gridData,int dx,int dy)
{
int x = gridData.Pos.X;
int y = gridData.Pos.Y;
bool ret = false;
using var pooledHaveType = THCollectionPool.GetHashSetHandle<int>(out var haveType);
for(int i = 0;i<=1;i++)
for (int j = 0; j <= 1; j++)
{
if (!mapData.GridMap.GetGridDataByPos(x + i*dx, y + j*dy, out var targetGrid)) return false;
//如果不是预设的当前gridData也就是另外3个待考察的目标只要不是preserve就说明无法成为国家公园)
if (i + j != 0 && targetGrid.Resource != ResourceType.Preserve) return false;
//将targetGrid的类型加入到hashset
haveType.Add(NationParkGridDataToPreserveType(targetGrid));
}
//如果类型没有达到3种也不能成为国家公园
if (haveType.Count < 3) return false;
return true;
}
public int NationParkGridDataToPreserveType(GridData gridData)
{
//山=0 树=1 浅=2 深=3
if (gridData.Feature == TerrainFeature.Mountain) return 0;
if (gridData.Vegetation == Vegetation.Trees) return 1;
if (gridData.Terrain == TerrainType.ShallowSea) return 2;
return 3;
}
//查询grid周围是否有某种resource
public bool CheckAroundGridHasSomeResourceBelongPlayer(MapData mapData, GridData gridData,PlayerData playerData, ResourceType buildingType)
{
//step #1 获得周围一圈的grid不管是不是我方
_aroundBuf ??= new List<GridData>();
_aroundBuf.Clear();
mapData.GridMap.GetAroundGridData(1,1,gridData, _aroundBuf);
//step #2 逐一判断每个周围的grid
foreach (var grid in _aroundBuf)
{
//如果不是player的领土continue
if (!mapData.CheckIfGidBelongPid(grid.Id, mapData.PlayerMap.SelfPlayerId)) continue;
if (grid.Resource == ResourceType.Port)
return true;
}
return false;
}
//确认一个城市是不是当前玩家的原始首都
public bool CheckCradleCapital(MapData map,PlayerData player,CityData city)
{
return player.CradleCityId == city.Id && city.IsCapital &&
map.GetPlayerDataByCityId(city.Id, out var player2) && player2.Id == player.Id;
}
}
}