Files
test/Assets/PerfectWorld/Scripts/Managers/IconAtlasProvider.cs
T
2025-09-24 08:57:39 +07:00

195 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using ModelRenderer.Scripts.Common;
using UnityEngine;
namespace PerfectWorld.Scripts.Managers
{
public static class IconAtlasProvider
{
private const string IconsFolder = "UI/Icons"; // under Resources
private const string IconListFileName = "iconlist_ivtrm"; // .txt
private const string IconAtlasTexName = "iconlist_ivtrm"; // .dds
private static readonly Dictionary<string, Sprite> _iconNameToSprite = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<int, string> _templateIdToIconName = new Dictionary<int, string>();
private static bool _initialized;
private static Texture2D _atlasTexture;
private static string _fallbackIconName = "unknown";
public static void EnsureInitialized()
{
if (_initialized) return;
_initialized = true;
try
{
LoadAtlasAndIcons();
}
catch (Exception ex)
{
Debug.LogWarning($"[IconAtlas] Failed to initialize: {ex.Message}");
}
}
public static Sprite GetSpriteByIconName(string iconName)
{
EnsureInitialized();
if (string.IsNullOrEmpty(iconName)) return GetFallback();
if (_iconNameToSprite.TryGetValue(iconName, out var sprite) && sprite != null) return sprite;
iconName = Path.GetFileNameWithoutExtension(iconName);
if (_iconNameToSprite.TryGetValue(iconName, out sprite) && sprite != null) return sprite;
return GetFallback();
}
public static Sprite GetSpriteForTemplateId(int templateId)
{
EnsureInitialized();
if (templateId <= 0) return GetFallback();
if (_templateIdToIconName.TryGetValue(templateId, out var name))
{
var s = GetSpriteByIconName(name);
if (s != null) return s;
}
string guessed = GuessIconNameFromTemplateId(templateId);
if (!string.IsNullOrEmpty(guessed))
{
_templateIdToIconName[templateId] = guessed;
var s = GetSpriteByIconName(guessed);
if (s != null) return s;
}
return GetFallback();
}
private static void LoadAtlasAndIcons()
{
_atlasTexture = Resources.Load<Texture2D>($"{IconsFolder}/{IconAtlasTexName}");
if (_atlasTexture == null)
{
Debug.LogWarning($"[IconAtlas] Could not load atlas texture '{IconAtlasTexName}' from Resources/{IconsFolder}");
return;
}
var textAsset = Resources.Load<TextAsset>($"{IconsFolder}/{IconListFileName}");
if (textAsset == null)
{
Debug.LogWarning($"[IconAtlas] Could not load icon list '{IconListFileName}.txt' from Resources/{IconsFolder}");
return;
}
ParseIconListAndCreateSprites(textAsset.bytes, _atlasTexture);
}
private static void ParseIconListAndCreateSprites(byte[] rawBytes, Texture2D atlas)
{
if (rawBytes == null || rawBytes.Length == 0 || atlas == null) return;
string fullText = ByteToStringUtils.ByteArrayToCP936String(rawBytes);
if (string.IsNullOrEmpty(fullText))
{
fullText = Encoding.UTF8.GetString(rawBytes);
}
var lines = fullText.Replace("\r\n", "\n").Replace("\r", "\n").Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length < 4)
{
Debug.LogWarning("[IconAtlas] icon list too short");
return;
}
int tileWidth = SafeParseInt(lines[0]);
int tileHeight = SafeParseInt(lines[1]);
int columns = SafeParseInt(lines[2]);
int count = SafeParseInt(lines[3]);
if (tileWidth <= 0 || tileHeight <= 0 || columns <= 0)
{
Debug.LogWarning($"[IconAtlas] Invalid header: w={tileWidth} h={tileHeight} cols={columns} count={count}");
return;
}
int created = 0;
for (int i = 4; i < lines.Length; i++)
{
string fileName = lines[i].Trim();
if (string.IsNullOrEmpty(fileName)) continue;
string nameNoExt = Path.GetFileNameWithoutExtension(fileName);
int index = i - 4;
int col = index % columns;
int row = index / columns;
int x = col * tileWidth;
int y = atlas.height - ((row + 1) * tileHeight);
if (y < 0) y = 0;
var rect = new Rect(x, y, tileWidth, tileHeight);
var pivot = new Vector2(0.5f, 0.5f);
try
{
var sprite = Sprite.Create(atlas, rect, pivot, 100.0f, 0, SpriteMeshType.FullRect);
_iconNameToSprite[nameNoExt] = sprite;
int tidPrefix = ExtractLeadingInt(nameNoExt);
if (tidPrefix > 0 && !_templateIdToIconName.ContainsKey(tidPrefix))
{
_templateIdToIconName[tidPrefix] = nameNoExt;
}
if (string.Equals(nameNoExt, "unknown", StringComparison.OrdinalIgnoreCase))
{
_fallbackIconName = nameNoExt;
}
created++;
}
catch (Exception ex)
{
Debug.LogWarning($"[IconAtlas] Failed to create sprite for '{fileName}': {ex.Message}");
}
}
Debug.Log($"[IconAtlas] Loaded {created} sprites from atlas '{IconAtlasTexName}' ({atlas.width}x{atlas.height}), tile {tileWidth}x{tileHeight}, cols {columns}, listed {count}");
}
private static Sprite GetFallback()
{
if (string.IsNullOrEmpty(_fallbackIconName)) return null;
_iconNameToSprite.TryGetValue(_fallbackIconName, out var s);
return s;
}
private static int SafeParseInt(string s)
{
if (string.IsNullOrEmpty(s)) return 0;
if (int.TryParse(s.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var v)) return v;
if (int.TryParse(s.Trim(), out v)) return v;
return 0;
}
private static int ExtractLeadingInt(string s)
{
if (string.IsNullOrEmpty(s)) return 0;
int i = 0;
while (i < s.Length && char.IsDigit(s[i])) i++;
if (i == 0) return 0;
if (int.TryParse(s.Substring(0, i), NumberStyles.Integer, CultureInfo.InvariantCulture, out var v)) return v;
return 0;
}
private static string GuessIconNameFromTemplateId(int templateId)
{
string tidStr = templateId.ToString(CultureInfo.InvariantCulture);
return _iconNameToSprite.Keys.FirstOrDefault(k => k.StartsWith(tidStr, StringComparison.Ordinal));
}
}
}