391 lines
15 KiB
C#
391 lines
15 KiB
C#
// BubbleData.cs
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Logic;
|
||
using Logic.Action;
|
||
using RuntimeData;
|
||
using TH1_Core.Events;
|
||
using TH1_Core.Managers;
|
||
using TH1_Logic.Core;
|
||
using TH1Renderer;
|
||
using UnityEngine;
|
||
using UnityEngine.Tilemaps;
|
||
using Object = UnityEngine.Object;
|
||
using Random = System.Random;
|
||
|
||
namespace TH1_Renderer
|
||
{
|
||
|
||
public enum BubbleType
|
||
{
|
||
None,
|
||
Gather,
|
||
Capture,
|
||
HeroUpgrade,
|
||
Upgrade,
|
||
Examine,
|
||
HeroSelect
|
||
}
|
||
|
||
//TODO 做BubbleData序列化相关的工作
|
||
[Serializable]
|
||
public struct BubbleData
|
||
{
|
||
public GridData Grid;
|
||
|
||
public UnitData Unit;
|
||
//public HintType HintType;
|
||
public BubbleType BubbleType;
|
||
// 用于显示自定义图标(如英雄头像)
|
||
public Sprite CustomSprite;
|
||
|
||
public BubbleData(GridData grid , BubbleType bubbleType, Sprite customSprite = null)
|
||
{
|
||
Grid = grid;
|
||
grid.MainSelfPlayerVisibleUnit(out Unit);
|
||
BubbleType = bubbleType;
|
||
CustomSprite = customSprite;
|
||
}
|
||
}
|
||
|
||
[Serializable]
|
||
public class InGameBubbleManager
|
||
{
|
||
//public HashSet<BubbleData> HintSet = new();
|
||
//private Dictionary<uint, BubbleData> _idToHintDict = new();
|
||
private uint _bubbleRendererIdCounter;
|
||
private GameObject _prefabROHint;
|
||
|
||
private Transform _selfTransform;
|
||
//private Dictionary<uint,BubbleRenderer> _ROHintRendererDict = new();
|
||
//存储可用的bubbleRendererId
|
||
private HashSet<uint> _availableRenderer;
|
||
private Dictionary<uint, BubbleRenderer> _dict;
|
||
//gid -> rendererid
|
||
private Dictionary<uint,uint> _gridHasBubble;
|
||
|
||
//每次开启一局游戏前,都要初始化的模块
|
||
public void Init(Transform selfTransform)
|
||
{
|
||
_bubbleRendererIdCounter = 0;
|
||
_prefabROHint = Resources.Load<GameObject>("Prefab/bubblePrefab");
|
||
_selfTransform = selfTransform;
|
||
_availableRenderer = new HashSet<uint>();
|
||
_dict = new Dictionary<uint, BubbleRenderer>();
|
||
_gridHasBubble = new Dictionary<uint,uint>();
|
||
}
|
||
|
||
//每局游戏开始前都要更新的
|
||
public void OnGameStart()
|
||
{
|
||
_bubbleRendererIdCounter = 0;
|
||
_availableRenderer = new HashSet<uint>();
|
||
_dict = new Dictionary<uint, BubbleRenderer>();
|
||
_gridHasBubble = new Dictionary<uint,uint>();
|
||
|
||
foreach (Transform child in _selfTransform.transform)
|
||
{
|
||
// 对每个子对象调用Destroy
|
||
Object.Destroy(child.gameObject);
|
||
}
|
||
}
|
||
|
||
public void OnGameClosed()
|
||
{
|
||
CloseAllBubble();
|
||
ClearAllRenderer();
|
||
}
|
||
|
||
public void ClearAllRenderer()
|
||
{
|
||
foreach (var t in _dict)
|
||
t.Value.Destroy();
|
||
_dict.Clear();
|
||
}
|
||
|
||
public uint BubbleDataIdGenerator()
|
||
{
|
||
return _bubbleRendererIdCounter++;
|
||
}
|
||
|
||
//每帧调用所有hintRenderer的update,并且将已经结束的Renderer回收
|
||
public void Update()
|
||
{
|
||
if (Main.MapData == null) return;
|
||
if (!Main.MapData.CurPlayer?.IsSelfPlayer() ?? true) return;
|
||
|
||
//遍历所有unit,查看哪些单位需要更新bubble显示
|
||
foreach (var unit in Main.MapData.UnitMap.UnitList)
|
||
{
|
||
//如果不是本机玩家
|
||
if (!unit.Player(Main.MapData, out var player)||!player.IsSelfPlayer()) continue;
|
||
|
||
if(!unit.Grid(Main.MapData,out var grid)) continue;
|
||
|
||
//准备升级行为的参数
|
||
var upgradeAction = ActionLogicFactory.GetActionLogic(new CommonActionId()
|
||
{ ActionType = CommonActionType.UnitAction, UnitActionType = UnitActionType.Upgrade });
|
||
var heroUpgradeAction = ActionLogicFactory.GetActionLogic(new CommonActionId()
|
||
{ ActionType = CommonActionType.UnitAction, UnitActionType = UnitActionType.HeroUpgrade });
|
||
var captureAction = ActionLogicFactory.GetActionLogic(new CommonActionId()
|
||
{ ActionType = CommonActionType.UnitAction, UnitActionType = UnitActionType.Capture });
|
||
var starfishAction = ActionLogicFactory.GetActionLogic(new CommonActionId()
|
||
{ ActionType = CommonActionType.UnitAction, UnitActionType = UnitActionType.Gather });
|
||
var treasureAction = ActionLogicFactory.GetActionLogic(new CommonActionId()
|
||
{ ActionType = CommonActionType.UnitAction, UnitActionType = UnitActionType.Examine });
|
||
var actionParam = new CommonActionParams(mapData: Main.MapData,
|
||
playerData: Main.MapData.PlayerMap.SelfPlayerData, unitData: unit,
|
||
gridData: unit.Grid(Main.MapData));
|
||
//先判断是否已经有按钮,如果有
|
||
|
||
|
||
bool upgrade = upgradeAction.CheckCan(actionParam);
|
||
bool heroUpgrade = heroUpgradeAction.CheckCan(actionParam);
|
||
bool capture = captureAction.CheckCan(actionParam);
|
||
bool treasure = treasureAction.CheckCan(actionParam);
|
||
bool starfish = starfishAction.CheckCan(actionParam);
|
||
bool showBubble = upgrade || heroUpgrade || capture || treasure || starfish;
|
||
BubbleType bubbleType = BubbleType.Upgrade;
|
||
if(heroUpgrade)bubbleType = BubbleType.HeroUpgrade;
|
||
if(capture)bubbleType = BubbleType.Capture;
|
||
if(treasure)bubbleType = BubbleType.Examine;
|
||
if(starfish)bubbleType = BubbleType.Gather;
|
||
|
||
//判断是否能执行升级行为,可以的话,播放bubble
|
||
if (showBubble)
|
||
{
|
||
if (_gridHasBubble.ContainsKey(grid.Id)) continue;
|
||
if(ShowBubble(new BubbleData(grid,bubbleType),out var rendererId))
|
||
_gridHasBubble.Add(grid.Id,rendererId);
|
||
}
|
||
//如果不应该播放bubble但是有个bubble,关掉他
|
||
//注:HeroSelect类型的气泡不依赖单位Action,需要单独检查
|
||
if(!showBubble)
|
||
{
|
||
if (_gridHasBubble.ContainsKey(grid.Id))
|
||
{
|
||
// 获取现有气泡类型,跳过HeroSelect类型
|
||
if (_dict.TryGetValue(_gridHasBubble[grid.Id], out var existingRenderer))
|
||
{
|
||
var existingBubbleData = existingRenderer.GetBubbleData();
|
||
if (existingBubbleData.BubbleType == BubbleType.HeroSelect)
|
||
continue; // HeroSelect气泡不由单位Action控制
|
||
existingRenderer.ForceClose();
|
||
}
|
||
RecycleBubble(_gridHasBubble[grid.Id],grid.Id);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
//遍历现在所有存在bubble的格子,如果格子上没有unit,让这个bubble自动关闭
|
||
//注:HeroSelect类型的气泡不依赖单位,跳过检查
|
||
var deleteList = new List<uint>();
|
||
foreach (var t in _gridHasBubble)
|
||
{
|
||
if (!Main.MapData.GridMap.GetGridDataByGid(t.Key, out var grid)) continue;
|
||
// 获取bubble类型
|
||
BubbleType bubbleType = BubbleType.None;
|
||
if (_dict.TryGetValue(t.Value, out var renderer))
|
||
{
|
||
bubbleType = renderer.GetBubbleData().BubbleType;
|
||
}
|
||
if (bubbleType == BubbleType.HeroSelect)
|
||
{
|
||
var selfPlayer = Main.MapData.PlayerMap?.SelfPlayerData;
|
||
var canShowHeroSelectBubble = ShouldShowHeroSelectBubble(Main.MapData, selfPlayer);
|
||
if (!canShowHeroSelectBubble)
|
||
{
|
||
renderer.ForceClose();
|
||
deleteList.Add(t.Key);
|
||
}
|
||
continue;
|
||
}
|
||
if (!grid.MainSelfPlayerVisibleUnit( out var _))
|
||
{
|
||
renderer.ForceClose();
|
||
deleteList.Add(t.Key);
|
||
}
|
||
|
||
}
|
||
//回收这些bubble
|
||
foreach (var t in deleteList)
|
||
if(_gridHasBubble.TryGetValue(t,out var v))
|
||
RecycleBubble(v,t);
|
||
|
||
}
|
||
|
||
public bool ShowBubble(BubbleData data,out uint rendererId)
|
||
{
|
||
|
||
//Step #1 获取一个可用的renderer
|
||
var id = GetAvailableRenderer();
|
||
_availableRenderer.Remove(id);
|
||
if (!_dict.TryGetValue(id, out var renderer))
|
||
{
|
||
Debug.Log($"Fatal error Showbubble can't find dict key id = {id}");
|
||
rendererId = 0;
|
||
return false;
|
||
}
|
||
|
||
rendererId = renderer.Id;
|
||
//Step #2 设置对应的renderer
|
||
return renderer.SetInfo(data,onClick:ClickBubble);
|
||
}
|
||
|
||
public uint GetAvailableRenderer()
|
||
{
|
||
|
||
//如果池子里没有可用对象,新创建一个
|
||
if (_availableRenderer.Count == 0)
|
||
{
|
||
var renderer = new BubbleRenderer(BubbleDataIdGenerator(),_prefabROHint,_selfTransform);
|
||
_availableRenderer.Add(renderer.Id);
|
||
_dict[renderer.Id] = renderer;
|
||
return renderer.Id;
|
||
}
|
||
//否则随便给一个可以用的
|
||
return _availableRenderer.First();
|
||
}
|
||
|
||
public void CloseAllBubble()
|
||
{
|
||
//遍历所有renderer,查询是否有不在available set里的,有的话就强制关闭
|
||
foreach (var t in _dict)
|
||
if (!_availableRenderer.Contains(t.Key))
|
||
{
|
||
t.Value.ForceClose();
|
||
_availableRenderer.Add(t.Key);
|
||
}
|
||
_gridHasBubble.Clear();
|
||
}
|
||
|
||
//被动由逻辑触发
|
||
public void RecycleBubble(uint rendererId,uint gid)
|
||
{
|
||
_availableRenderer.Add(rendererId);
|
||
_gridHasBubble.Remove(gid);
|
||
|
||
|
||
}
|
||
|
||
|
||
//由玩家主动触发
|
||
public void ClickBubble(uint rendererId,uint gid)
|
||
{
|
||
// 获取气泡类型
|
||
var isHeroSelectBubble = false;
|
||
if (_dict.TryGetValue(rendererId, out var renderer))
|
||
{
|
||
isHeroSelectBubble = renderer.GetBubbleData().BubbleType == BubbleType.HeroSelect;
|
||
}
|
||
|
||
RecycleBubble(rendererId,gid);
|
||
//UI收回
|
||
Main.Instance.MapInteractionLogic.CancelAllHighlight();
|
||
// HeroSelect气泡不发送HideAllUIInfo,因为会关闭刚打开的Hero界面
|
||
if (!isHeroSelectBubble)
|
||
{
|
||
EventManager.Publish(new HideAllUIInfo(){});
|
||
}
|
||
|
||
}
|
||
|
||
// HeroSelect 气泡的显示条件:有待选英雄,或者可以购买 SecondHero/ThirdHero 文化卡(且尚未拥有)
|
||
private static bool ShouldShowHeroSelectBubble(MapData mapData, PlayerData playerData)
|
||
{
|
||
if (mapData == null || playerData == null) return false;
|
||
if (playerData.PlayerHeroData?.GetHeroButtonHint() == true) return true;
|
||
var cultureInfo = playerData.PlayerCultureInfo;
|
||
if (cultureInfo == null) return false;
|
||
if (CanBuyHeroCultureCard(mapData, playerData, cultureInfo, CultureCardType.SecondHero)) return true;
|
||
if (CanBuyHeroCultureCard(mapData, playerData, cultureInfo, CultureCardType.ThirdHero)) return true;
|
||
return false;
|
||
}
|
||
|
||
private static bool CanBuyHeroCultureCard(MapData mapData, PlayerData playerData, PlayerCultureInfo cultureInfo, CultureCardType cardType)
|
||
{
|
||
// 已经拥有则不需要再买
|
||
if (cultureInfo.CultureCardList != null && cultureInfo.CultureCardList.Contains(cardType)) return false;
|
||
return cultureInfo.CheckCanBuyCultureCard(mapData, playerData, cardType);
|
||
}
|
||
|
||
public void TurnStartSetBubble(MapData mapData, PlayerData playerData)
|
||
{
|
||
//Debug.Log($"[Bubble] TurnStartSetBubble called for player {playerData.Id}, IsSelfPlayer={playerData.IsSelfPlayer()}");
|
||
|
||
// 只处理本机玩家
|
||
if (!playerData.IsSelfPlayer())
|
||
{
|
||
//Debug.Log("[Bubble] Not self player, returning");
|
||
return;
|
||
}
|
||
|
||
// 检查是否有待选择的英雄
|
||
var heroData = playerData.PlayerHeroData;
|
||
//Debug.Log($"[Bubble] PlayerHeroData is null: {heroData == null}");
|
||
//if (heroData != null)
|
||
//{
|
||
// Debug.Log($"[Bubble] HeroCount={heroData.HeroCount}, MaxHeroCount={heroData.MaxHeroCount}, GetHeroButtonHint={heroData.GetHeroButtonHint()}");
|
||
//}
|
||
|
||
if (!ShouldShowHeroSelectBubble(mapData, playerData))
|
||
{
|
||
//Debug.Log("[Bubble] No hero to select and no hero culture card buyable, returning");
|
||
return;
|
||
}
|
||
|
||
// 获取玩家首都位置
|
||
if (!mapData.GetCapitalCityDataByPlayerId(playerData.Id, out var capitalCity))
|
||
{
|
||
//Debug.Log("[Bubble] No capital city found, returning");
|
||
return;
|
||
}
|
||
//Debug.Log($"[Bubble] Found capital city: {capitalCity?.Id}");
|
||
|
||
var capitalGrid = capitalCity.Grid(mapData);
|
||
if (capitalGrid == null)
|
||
{
|
||
//Debug.Log("[Bubble] Capital grid is null, returning");
|
||
return;
|
||
}
|
||
//Debug.Log($"[Bubble] Capital grid: {capitalGrid.Id}");
|
||
|
||
// 获取英雄按钮的sprite
|
||
var heroSprite = playerData.PlayerHeroData.GetHeroButtonSprite();
|
||
//Debug.Log($"[Bubble] Hero sprite is null: {heroSprite == null}");
|
||
|
||
// 创建BubbleData并显示
|
||
var bubbleData = new BubbleData(capitalGrid, BubbleType.HeroSelect, heroSprite);
|
||
//Debug.Log($"[Bubble] Created BubbleData for HeroSelect at grid {capitalGrid.Id}");
|
||
|
||
// 如果该位置已有bubble,先关闭它
|
||
if (_gridHasBubble.ContainsKey(capitalGrid.Id))
|
||
{
|
||
//Debug.Log($"[Bubble] Grid {capitalGrid.Id} already has bubble, recycling");
|
||
if (_dict.TryGetValue(_gridHasBubble[capitalGrid.Id], out var existingRenderer))
|
||
existingRenderer.ForceClose();
|
||
RecycleBubble(_gridHasBubble[capitalGrid.Id], capitalGrid.Id);
|
||
}
|
||
|
||
// 显示新bubble
|
||
//Debug.Log("[Bubble] Calling ShowBubble...");
|
||
if (ShowBubble(bubbleData, out var rendererId))
|
||
{
|
||
_gridHasBubble.Add(capitalGrid.Id, rendererId);
|
||
//Debug.Log($"[Bubble] Successfully showed bubble with rendererId {rendererId}");
|
||
}
|
||
else
|
||
{
|
||
//Debug.Log("[Bubble] ShowBubble returned false!");
|
||
}
|
||
}
|
||
|
||
}
|
||
} |