212 lines
8.4 KiB
C#
212 lines
8.4 KiB
C#
|
|
using BepInEx;
|
|||
|
|
using HarmonyLib; // Necesario para Traverse
|
|||
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using TMPro;
|
|||
|
|
using UnityEngine.UI; // Necesario para Outline
|
|||
|
|
|
|||
|
|
namespace StrategicMapPlus
|
|||
|
|
{
|
|||
|
|
[BepInPlugin("com.mod.strategicmapplus", "Strategic Map Plus", "1.0.0")]
|
|||
|
|
public class StrategicMapMod : BaseUnityPlugin
|
|||
|
|
{
|
|||
|
|
private float timer = 0f;
|
|||
|
|
// Escaneamos cada 0.5 segundos (balance perfecto entre respuesta y rendimiento)
|
|||
|
|
private float interval = 0.5f;
|
|||
|
|
|
|||
|
|
void Awake()
|
|||
|
|
{
|
|||
|
|
Logger.LogInfo("Strategic Map Plus: Iniciado correctamente.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Update()
|
|||
|
|
{
|
|||
|
|
timer += Time.deltaTime;
|
|||
|
|
if (timer < interval) return;
|
|||
|
|
timer = 0f;
|
|||
|
|
|
|||
|
|
ScanStrategicMap();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void ScanStrategicMap()
|
|||
|
|
{
|
|||
|
|
// Buscamos el contenedor de regiones
|
|||
|
|
GameObject regionsRoot = GameObject.Find("UI/2DRoot/Layout/Limit/FrontDialogs/StrategicView/Root/Regions");
|
|||
|
|
|
|||
|
|
if (regionsRoot == null || !regionsRoot.activeInHierarchy) return;
|
|||
|
|
|
|||
|
|
foreach (Transform region in regionsRoot.transform)
|
|||
|
|
{
|
|||
|
|
// Buscamos el Army (contenedor lógico)
|
|||
|
|
Transform army = region.Find("Army");
|
|||
|
|
if (army == null) continue;
|
|||
|
|
|
|||
|
|
// Buscamos el componente del juego (DefenseCountRenderer) por nombre
|
|||
|
|
Component defenseRenderer = army.GetComponent("iron.match.renderers.strategicview.DefenseCountRenderer");
|
|||
|
|
|
|||
|
|
// Buscamos el contenedor visual "Root" para anclar nuestro texto
|
|||
|
|
Transform visualRoot = army.Find("Root");
|
|||
|
|
Transform targetParent = (visualRoot != null) ? visualRoot : army;
|
|||
|
|
|
|||
|
|
if (defenseRenderer != null)
|
|||
|
|
{
|
|||
|
|
ProcessRegion((MonoBehaviour)defenseRenderer, targetParent);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void ProcessRegion(MonoBehaviour renderer, Transform parentTransform)
|
|||
|
|
{
|
|||
|
|
// 1. Configurar o Buscar el Visualizador
|
|||
|
|
var visualizer = parentTransform.GetComponent<StrategicOrderDisplay>();
|
|||
|
|
if (visualizer == null)
|
|||
|
|
{
|
|||
|
|
visualizer = parentTransform.gameObject.AddComponent<StrategicOrderDisplay>();
|
|||
|
|
visualizer.Initialize();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. Acceder al Modelo de datos usando Reflection (Traverse)
|
|||
|
|
var traverse = Traverse.Create(renderer);
|
|||
|
|
var model = traverse.Property("model").GetValue();
|
|||
|
|
|
|||
|
|
if (model == null)
|
|||
|
|
{
|
|||
|
|
visualizer.UpdateOrder("", false);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. Obtener la lista de hijos (Children)
|
|||
|
|
// Ya sabemos que es un Campo llamado "Children" (con mayúscula)
|
|||
|
|
var children = Traverse.Create(model).Field("Children").GetValue() as IEnumerable;
|
|||
|
|
// Fallback a minúscula por si acaso cambia en otra versión
|
|||
|
|
if (children == null) children = Traverse.Create(model).Field("children").GetValue() as IEnumerable;
|
|||
|
|
|
|||
|
|
string finalText = "";
|
|||
|
|
bool foundOrder = false;
|
|||
|
|
|
|||
|
|
if (children != null)
|
|||
|
|
{
|
|||
|
|
foreach (var child in children)
|
|||
|
|
{
|
|||
|
|
// 4. Leer Atributos Crudos
|
|||
|
|
var childTrav = Traverse.Create(child);
|
|||
|
|
var attributesObj = childTrav.Field("attributes").GetValue();
|
|||
|
|
|
|||
|
|
if (attributesObj != null)
|
|||
|
|
{
|
|||
|
|
// Accedemos al diccionario interno 'data'
|
|||
|
|
var valuesDict = Traverse.Create(attributesObj).Field("data").GetValue() as IDictionary;
|
|||
|
|
|
|||
|
|
if (valuesDict != null)
|
|||
|
|
{
|
|||
|
|
// ID 400003 = IronType (Tipo de unidad)
|
|||
|
|
if (valuesDict.Contains(400003))
|
|||
|
|
{
|
|||
|
|
// Desempaquetamos el MutableAttribute
|
|||
|
|
object typeContainer = valuesDict[400003];
|
|||
|
|
object typeVal = Traverse.Create(typeContainer).Property("Value").GetValue();
|
|||
|
|
string typeName = typeVal?.ToString();
|
|||
|
|
|
|||
|
|
if (typeName == "Order")
|
|||
|
|
{
|
|||
|
|
// ¡Es una orden!
|
|||
|
|
|
|||
|
|
// ID 400038 = OrderType (Tipo de orden)
|
|||
|
|
string orderCode = "?";
|
|||
|
|
if (valuesDict.Contains(400038))
|
|||
|
|
{
|
|||
|
|
object orderContainer = valuesDict[400038];
|
|||
|
|
object orderVal = Traverse.Create(orderContainer).Property("Value").GetValue();
|
|||
|
|
|
|||
|
|
var list = orderVal as IList;
|
|||
|
|
if (list != null && list.Count > 0)
|
|||
|
|
{
|
|||
|
|
orderCode = list[0].ToString();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ID 400039 = HasStar (Es especial)
|
|||
|
|
bool isStar = false;
|
|||
|
|
if (valuesDict.Contains(400039))
|
|||
|
|
{
|
|||
|
|
object starContainer = valuesDict[400039];
|
|||
|
|
object starVal = Traverse.Create(starContainer).Property("Value").GetValue();
|
|||
|
|
|
|||
|
|
if (starVal != null && starVal.ToString().ToLower() == "true") isStar = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
finalText = GetOrderShortName(orderCode, isStar);
|
|||
|
|
foundOrder = true;
|
|||
|
|
break; // Solo mostramos 1 orden por región
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 5. Actualizar UI
|
|||
|
|
visualizer.UpdateOrder(finalText, foundOrder);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static string GetOrderShortName(string typeName, bool isSpecial)
|
|||
|
|
{
|
|||
|
|
string symbol = "";
|
|||
|
|
string star = isSpecial ? "*" : "";
|
|||
|
|
|
|||
|
|
if (typeName.Contains("March")) symbol = "M";
|
|||
|
|
else if (typeName.Contains("Defense")) symbol = "D";
|
|||
|
|
else if (typeName.Contains("Support")) symbol = "S";
|
|||
|
|
else if (typeName.Contains("Raid")) symbol = "R";
|
|||
|
|
else if (typeName.Contains("Consolidate")) symbol = "$"; // Usamos $ porque parece una moneda/corona
|
|||
|
|
else symbol = typeName.Substring(0, 1);
|
|||
|
|
|
|||
|
|
return symbol + star;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- COMPONENTE VISUAL ---
|
|||
|
|
public class StrategicOrderDisplay : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
private TextMeshProUGUI orderText;
|
|||
|
|
private GameObject displayObj;
|
|||
|
|
|
|||
|
|
public void Initialize()
|
|||
|
|
{
|
|||
|
|
if (displayObj != null) return;
|
|||
|
|
|
|||
|
|
displayObj = new GameObject("OrderDisplay_Mod");
|
|||
|
|
displayObj.transform.SetParent(this.transform, false);
|
|||
|
|
|
|||
|
|
// Ajustamos posición: X=60 (derecha del escudo), Z=-100 (encima de todo)
|
|||
|
|
displayObj.transform.localPosition = new Vector3(60f, 0f, -100f);
|
|||
|
|
|
|||
|
|
orderText = displayObj.AddComponent<TextMeshProUGUI>();
|
|||
|
|
orderText.fontSize = 24;
|
|||
|
|
orderText.alignment = TextAlignmentOptions.Left;
|
|||
|
|
// Amarillo Dorado (#FFD700) - Clásico de Game of Thrones
|
|||
|
|
orderText.color = new Color(1f, 0.84f, 0f);
|
|||
|
|
orderText.fontStyle = FontStyles.Bold;
|
|||
|
|
orderText.enableWordWrapping = false;
|
|||
|
|
|
|||
|
|
// Añadimos borde negro para que se lea bien sobre cualquier fondo
|
|||
|
|
var outline = displayObj.AddComponent<Outline>();
|
|||
|
|
outline.effectColor = Color.black;
|
|||
|
|
outline.effectDistance = new Vector2(1, -1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void UpdateOrder(string text, bool visible)
|
|||
|
|
{
|
|||
|
|
if (displayObj == null) Initialize();
|
|||
|
|
|
|||
|
|
// Gestión eficiente de SetActive para no parpadear
|
|||
|
|
if (displayObj.activeSelf != visible) displayObj.SetActive(visible);
|
|||
|
|
|
|||
|
|
if (visible && orderText != null && orderText.text != text)
|
|||
|
|
{
|
|||
|
|
orderText.text = text;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|