Files
test/Assets/PerfectWorld/Scripts/UI/MiniMap/CDlgMiniMap.cs
T
2026-05-13 10:15:29 +07:00

262 lines
10 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.Collections.Generic;
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Extensions;
using BrewMonster.UI;
using CSNetwork.GPDataType;
using TMPro;
using UnityEngine;
using UnityEngine.U2D;
using UnityEngine.UI;
namespace PerfectWorld.UI.MiniMap
{
public class CDlgMiniMap : AUIDialog
{
public struct MARK
{
public int nNPC;
public string strName;
public A3DVECTOR3 vecPos;
public int mapID; // 地图ID // map ID (exposed for USER_LAYOUT save)
public MARK(int nNPC, string strName, A3DVECTOR3 vecPos, int mapID)
{
this.nNPC = nNPC;
this.strName = strName;
this.vecPos = vecPos;
this.mapID = mapID;
}
}
[SerializeField] private Vector3 _debugHostPlayerPos;
[SerializeField] private RectTransform _hostPlayerIcon;
[SerializeField] private byte nRow, nCol; // number of rows and cols in the current map instances.txt
[SerializeField] private TMP_Text txtHostPos;
[SerializeField] private Image _imageMiniMapPrefab;
[SerializeField] private List<Image> _listImageMiniMap = new();
[SerializeField] private RectTransform _transformMiniMapParent;
[SerializeField] private Button _worldMapButton;
// reference to unity sprite atlas
[SerializeField] private SpriteAtlas _spriteAtlas;
private List<MARK> m_vecMark = new();
private List<MARK> m_vecNPCMark = new();
private Dictionary<string, Sprite> m_TexMap = new();
private List<string> _texToDelete = new(); // list of texture to delete from m_TexMap, use in update functions.
private float m_fZoom = 1.0f;
private bool m_bShowMark = true;
private bool m_bShowTargetArrow = true;
// map state
private bool isShowMiniMap = true;
CECHostPlayer m_pHostPlayer;
private float coordinateFactor = 0.5f; // the factor to convert the world coordinates to the mini map coordinates.
Vector3Int _lastIntHostPos = Vector3Int.zero;
private int m_nMode; // TODO: currently, there is only get logic, not set logic
// PC DlgMiniMap.cpp: GetStringFromTable(604), GetStringFromTable(1330 + nTimeIndex), FixFrame(nTimeItem)
private const int StrIdSystemTimeFormat = 604;
private const int StrIdShichenBase = 1330;
private enum MinimapTimeSprite
{
TIME_DAY = 0,
TIME_MORNING,
TIME_DUSK,
TIME_NIGHT,
}
/// <summary>十二时辰名(表缺失时回退,与 PC 1330+i 顺序一致:子丑寅…) // Fallback 12 double-hours if string table missing (same order as PC 1330+i)</summary>
static readonly string[] s_FallbackShichenNames =
{
"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥", // Zi, Chou, ...
};
[Header("Set data show time in mini map")]
[SerializeField] private TMP_Text _txtSystemTime;
[SerializeField] private Image _imgSystemTime;
[Tooltip("PC order: TIME_DAY, TIME_MORNING, TIME_DUSK, TIME_NIGHT (DlgMiniMap enum)")]
[SerializeField] private Sprite[] _systemTimeSprites;
private void Awake()
{
// LoadAllMiniMapTextures();
_worldMapButton.onClick.AddListener(OnMiniMapClicked);
}
void Update()
{
UpdateMiniMap();
UpdateSystemClockFromPcMiniMapLogic();
}
/// <summary>
/// We keep the player icon at the center of the minimap. Then we update the position of the map itself.
/// TODO: We have to keep track of the NPC icons on the map also.
/// </summary>
private void UpdateMiniMap()
{
m_pHostPlayer = GetHostPlayer();
if (m_pHostPlayer == null) return;
Transform hostTransform = m_pHostPlayer.transform;
Vector3 vecPosHost = hostTransform.position;
Vector3Int currentIntHostPos = new Vector3Int(Mathf.RoundToInt(vecPosHost.x) / 10 + 400, Mathf.RoundToInt(vecPosHost.y) / 10, Mathf.RoundToInt(vecPosHost.z) / 10 + 550);
if (currentIntHostPos != _lastIntHostPos)
{
txtHostPos.text = $"{currentIntHostPos.x}, {currentIntHostPos.z}, ↑{currentIntHostPos.y}";
_lastIntHostPos = currentIntHostPos;
}
Vector2 hostPlayerPos = new Vector2(vecPosHost.x * coordinateFactor, vecPosHost.z * coordinateFactor);
_transformMiniMapParent.anchoredPosition = -hostPlayerPos;
_hostPlayerIcon.localRotation = Quaternion.Euler(0, 0, -hostTransform.localRotation.eulerAngles.y);
}
/// <summary>
/// PC DlgMiniMap.cpp Render: nTimeIndex, Format(604,...), int(v*24), FixFrame(nTimeItem).
/// </summary>
void UpdateSystemClockFromPcMiniMapLogic()
{
var sun = CECSunMoon.Instance;
if (sun == null)
return;
var inGame = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan();
float fDNFactor = sun.GetDNFactor();
float fDNFactorDest = sun.GetDNFactorDest();
float v = sun.GetTimeOfTheDay();
int nTimeIndex = (int)(12.0f * v + 0.5f) % 12;
MinimapTimeSprite nTimeItem;
if (fDNFactor == 0.0f)
nTimeItem = MinimapTimeSprite.TIME_DAY;
else if (fDNFactor == 1.0f)
nTimeItem = MinimapTimeSprite.TIME_NIGHT;
else
nTimeItem = fDNFactorDest == 1.0f ? MinimapTimeSprite.TIME_DUSK : MinimapTimeSprite.TIME_MORNING;
if (_imgSystemTime != null && _systemTimeSprites != null &&
_systemTimeSprites.Length > (int)nTimeItem && _systemTimeSprites[(int)nTimeItem] != null)
_imgSystemTime.sprite = _systemTimeSprites[(int)nTimeItem];
string shichen = inGame != null ? GetStringFromTable(StrIdShichenBase + nTimeIndex) : null;
if (string.IsNullOrEmpty(shichen))
shichen = s_FallbackShichenNames[nTimeIndex];
int hour = (int)(v * 24.0f);
string fmt = inGame != null ? GetStringFromTable(StrIdSystemTimeFormat) : null;
if (string.IsNullOrEmpty(fmt))
fmt = "{0}{1}时)"; // 与常见 PC 客户端格式一致 // Typical PC client style: name (hour)
string strText = FormatPrintf(fmt, shichen, hour);
if (_txtSystemTime != null)
_txtSystemTime.text = strText;
BMLogger.Log($"[Cuong] {strText}");
}
/// <summary>
/// Get the Host Player instance.
/// </summary>
/// <returns></returns>
private CECHostPlayer GetHostPlayer()
{
return CECGameRun.Instance.GetHostPlayer();
}
// change radar mode
public int GetMode() { return m_nMode; }
/// <summary>Returns the list of user-placed marks on the minimap (for layout save/load).</summary>
public List<MARK> GetMarks() => m_vecMark;
/// <summary>User click on the minimap, we'll show the world map.</summary>
public void OnMiniMapClicked()
{
var dlg = CECUIManager.Instance.ShowUI("Win_WorldMap") as DlgWorldMap;
// dlg?.Show(true);
// dlg?.OnInitDialog();
}
/// <summary>
/// Call this function when user enter the game world (after select role or when use GOTO to jump to a new instance).
/// This function will get the world instance data and setup the mini map.
/// </summary>
public async void InitializeMiniMap()
{
// get current world instance
var idInstance = CECGameRun.Instance?.GetWorld()?.GetInstanceID() ?? 161;
var worldInstance = EC_Game.GetGameRun()?.GetInstance(idInstance);
if (worldInstance == null)
{
BMLogger.LogError("InitializeMiniMap: worldInstance is null");
return;
}
// set the number of rows and columns of the mini map
nRow = (byte)worldInstance.GetRowNum();
nCol = (byte)worldInstance.GetColNum();
// use Addressable to load all the textures of the mini map
_spriteAtlas = await AddressableManager.Instance.LoadSpriteAtlasAsync($"minimaps/{idInstance}");
if (_spriteAtlas == null)
{
BMLogger.LogError("InitializeMiniMap: sprite atlas is null");
return;
}
LoadAllMiniMapTextures();
}
// keep this so we can load all textures of other map also.
[ContextMenu("LoadAllMiniMapTextures")]
public void LoadAllMiniMapTextures()
{
// delete all images in parent
foreach(Transform child in _transformMiniMapParent)
{
Destroy(child.gameObject);
}
Sprite pSprite = null;
for(int r = 0; r < nRow + 3; r++)
{
for(int c = 0; c < nCol + 3; c++)
{
string strIndex = $"{r:D2}{c:D2}";
pSprite = _spriteAtlas.GetSprite(strIndex);
var image = Instantiate(_imageMiniMapPrefab, _transformMiniMapParent);
image.sprite = pSprite;
image.name = strIndex;
image.gameObject.SetActive(true);
}
}
}
#if UNITY_EDITOR
// this is for debuging/testing while this feature was in development
[ContextMenu("MoveHostPlayerIconToPos")]
public void MoveHostPlayerIconToPos()
{
Vector2 hostPlayerPos = new Vector2(_debugHostPlayerPos.x / 2, _debugHostPlayerPos.z / 2);
_transformMiniMapParent.anchoredPosition = -hostPlayerPos;
}
#endif
}
}