diff --git a/Class1.cs b/Class1.cs index 51e360b..bd5ef63 100644 --- a/Class1.cs +++ b/Class1.cs @@ -1,212 +1,273 @@ using BepInEx; -using HarmonyLib; // Necesario para Traverse +using HarmonyLib; using System.Collections; using System.Collections.Generic; +using System.Linq; using UnityEngine; +using UnityEngine.UI; using TMPro; -using UnityEngine.UI; // Necesario para Outline namespace StrategicMapPlus { - [BepInPlugin("com.mod.strategicmapplus", "Strategic Map Plus", "1.0.0")] + [BepInPlugin("com.mod.strategicmapplus", "Strategic Map Plus", "28.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; + private bool texturesLoaded = false; + + public static Dictionary SpriteCache = new Dictionary(); + private HashSet reportedMissingKeys = new HashSet(); void Awake() { - Logger.LogInfo("Strategic Map Plus: Iniciado correctamente."); + Logger.LogInfo("Strategic Map Plus v28: Posición 0,0,0."); } void Update() { + if (!texturesLoaded && Time.time > 3f) LoadTextures(); + timer += Time.deltaTime; if (timer < interval) return; timer = 0f; - ScanStrategicMap(); + if (texturesLoaded) ScanStrategicMap(); + } + + void LoadTextures() + { + Texture2D[] allTextures = Resources.FindObjectsOfTypeAll(); + Dictionary textureLookup = new Dictionary(); + + foreach (var tex in allTextures) + { + if (tex != null && !string.IsNullOrEmpty(tex.name)) + { + if (!textureLookup.ContainsKey(tex.name)) textureLookup.Add(tex.name, tex); + } + } + + int loadedCount = 0; + + foreach (var kvp in Textures.TextureMap) + { + string logicKey = kvp.Key; + string texName = kvp.Value; + + if (textureLookup.ContainsKey(texName)) + { + Texture2D tex = textureLookup[texName]; + Sprite s = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); + SpriteCache[logicKey] = s; + loadedCount++; + } + } + + if (loadedCount > 0) texturesLoaded = true; } 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(); + var visualizer = parentTransform.GetComponent(); if (visualizer == null) { - visualizer = parentTransform.gameObject.AddComponent(); + visualizer = parentTransform.gameObject.AddComponent(); 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.Hide(); return; } - 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; + string orderType = ""; + bool isSpecial = false; + int strength = 0; + bool found = 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) + if (valuesDict != null && valuesDict.Contains(400003)) { - // ID 400003 = IronType (Tipo de unidad) - if (valuesDict.Contains(400003)) + object typeCont = valuesDict[400003]; + object typeVal = Traverse.Create(typeCont).Property("Value").GetValue(); + + if (typeVal?.ToString() == "Order") { - // Desempaquetamos el MutableAttribute - object typeContainer = valuesDict[400003]; - object typeVal = Traverse.Create(typeContainer).Property("Value").GetValue(); - string typeName = typeVal?.ToString(); - - if (typeName == "Order") + if (valuesDict.Contains(400038)) // OrderType { - // ¡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 + object oCont = valuesDict[400038]; + object oVal = Traverse.Create(oCont).Property("Value").GetValue(); + var list = oVal as IList; + if (list != null && list.Count > 0) orderType = list[0].ToString(); } + if (valuesDict.Contains(400039)) // HasStar + { + object sCont = valuesDict[400039]; + object sVal = Traverse.Create(sCont).Property("Value").GetValue(); + if (sVal != null && sVal.ToString().ToLower() == "true") isSpecial = true; + } + if (valuesDict.Contains(400004)) // Strength + { + object strCont = valuesDict[400004]; + object strVal = Traverse.Create(strCont).Property("Value").GetValue(); + int.TryParse(strVal?.ToString(), out strength); + } + found = true; + break; } } } } } - // 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(); - 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.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) + if (!found) { - orderText.text = text; + visualizer.Hide(); + return; + } + + // Generar clave lógica + string generatedKey = $"{orderType}_{strength}"; + if (isSpecial) generatedKey += "_Star"; + + if (SpriteCache.ContainsKey(generatedKey)) + { + visualizer.ShowIcon(SpriteCache[generatedKey]); + } + else + { + if (!reportedMissingKeys.Contains(generatedKey)) + { + Logger.LogWarning($"[FALTA MAPEO] El juego pide: '{generatedKey}'"); + reportedMissingKeys.Add(generatedKey); + } + visualizer.ShowText(generatedKey); } } } + + // --- VISUALIZADOR --- + public class StrategicDisplay : MonoBehaviour + { + private GameObject displayObj; + private Image iconImage; + private TextMeshProUGUI debugText; + private bool isInitialized = false; + + public void Initialize() + { + if (isInitialized && displayObj != null) return; + if (displayObj != null) Destroy(displayObj); + + try + { + displayObj = new GameObject("OrderIcon_Mod"); + displayObj.transform.SetParent(this.transform, false); + + // POSICIÓN EXACTA 0,0,0 + displayObj.transform.localPosition = Vector3.zero; + + // Aseguramos capa correcta + displayObj.layer = this.gameObject.layer; + + RectTransform rt = displayObj.AddComponent(); + rt.sizeDelta = new Vector2(45, 45); // Tamaño del icono + + iconImage = displayObj.AddComponent(); + iconImage.raycastTarget = false; + iconImage.color = Color.white; + + // Backup Text + GameObject textObj = new GameObject("BackupText"); + textObj.transform.SetParent(displayObj.transform, false); + textObj.layer = this.gameObject.layer; + + RectTransform textRt = textObj.AddComponent(); + textRt.anchoredPosition = Vector2.zero; + + debugText = textObj.AddComponent(); + debugText.alignment = TextAlignmentOptions.Center; + debugText.fontSize = 14; + debugText.fontStyle = FontStyles.Bold; + debugText.color = Color.magenta; + + // CRUCIAL: Ponemos el icono al final de la lista de hijos para que se pinte ENCIMA + displayObj.transform.SetAsLastSibling(); + + isInitialized = true; + } + catch (System.Exception e) + { + Debug.LogWarning("Error UI: " + e.Message); + if (displayObj != null) Destroy(displayObj); + isInitialized = false; + } + } + + public void Hide() + { + if (displayObj != null && displayObj.activeSelf) displayObj.SetActive(false); + } + + public void ShowIcon(Sprite s) + { + if (!isInitialized) Initialize(); + if (!isInitialized) return; + + // Aseguramos orden de dibujado (Encima de todo) + displayObj.transform.SetAsLastSibling(); + + if (!displayObj.activeSelf) displayObj.SetActive(true); + + if (s != null) + { + iconImage.sprite = s; + iconImage.enabled = true; + if (debugText != null) debugText.text = ""; + } + } + + public void ShowText(string t) + { + if (!isInitialized) Initialize(); + if (!isInitialized) return; + + displayObj.transform.SetAsLastSibling(); + + if (!displayObj.activeSelf) displayObj.SetActive(true); + + if (iconImage != null) iconImage.enabled = false; + if (debugText != null) debugText.text = t; + } + } } \ No newline at end of file diff --git a/Textures.cs b/Textures.cs new file mode 100644 index 0000000..fcac604 --- /dev/null +++ b/Textures.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StrategicMapPlus +{ + public class Textures +{ + // ----------------------------------------------------------------------- + // --- ZONA DE CONFIGURACIÓN --- + // ----------------------------------------------------------------------- + public static Dictionary TextureMap = new Dictionary() + { + // --- CONSOLIDAR PODER (Fuerza suele ser 0) --- + { "ConsolidatePower_0", "T_tkn_Order_ConsolidatePower" }, + { "ConsolidatePower_0_Star", "T_tkn_Order_ConsolidatePowerStar" }, + + // March + {"March_130", "T_tkn_Order_MarchPlus0" }, + {"March_129_Star", "T_tkn_Order_MarchPlus1" }, + + // Defense + {"Defense_124_Star", "T_tkn_Order_DefendPlus2" }, + }; + // ----------------------------------------------------------------------- + + } +} diff --git a/obj/Debug/net472/StrategicView-Plus.AssemblyInfo.cs b/obj/Debug/net472/StrategicView-Plus.AssemblyInfo.cs index 165bbe1..a48e36e 100644 --- a/obj/Debug/net472/StrategicView-Plus.AssemblyInfo.cs +++ b/obj/Debug/net472/StrategicView-Plus.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("StrategicView-Plus")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ae0a5853b586351c17dcfa88c3344792a4628614")] [assembly: System.Reflection.AssemblyProductAttribute("StrategicView-Plus")] [assembly: System.Reflection.AssemblyTitleAttribute("StrategicView-Plus")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/obj/Debug/net472/StrategicView-Plus.AssemblyInfoInputs.cache b/obj/Debug/net472/StrategicView-Plus.AssemblyInfoInputs.cache index 02753d1..8b16415 100644 --- a/obj/Debug/net472/StrategicView-Plus.AssemblyInfoInputs.cache +++ b/obj/Debug/net472/StrategicView-Plus.AssemblyInfoInputs.cache @@ -1 +1 @@ -273bd5731decc3a9326eaf60c8347c8c52258602c6bbbe66e14477d5903fa4e0 +2b75dcbe8616bb42f12edaec3fc9bc49fdb1555dee5fe1f058109fdefbb47f6b diff --git a/obj/Debug/net472/StrategicView-Plus.assets.cache b/obj/Debug/net472/StrategicView-Plus.assets.cache index a069305..0a62f40 100644 Binary files a/obj/Debug/net472/StrategicView-Plus.assets.cache and b/obj/Debug/net472/StrategicView-Plus.assets.cache differ diff --git a/obj/Debug/net472/StrategicView-Plus.csproj.AssemblyReference.cache b/obj/Debug/net472/StrategicView-Plus.csproj.AssemblyReference.cache index 553ad8b..950b0be 100644 Binary files a/obj/Debug/net472/StrategicView-Plus.csproj.AssemblyReference.cache and b/obj/Debug/net472/StrategicView-Plus.csproj.AssemblyReference.cache differ diff --git a/obj/Debug/net472/StrategicView-Plus.csproj.CoreCompileInputs.cache b/obj/Debug/net472/StrategicView-Plus.csproj.CoreCompileInputs.cache index f9b3594..8c4e9e9 100644 --- a/obj/Debug/net472/StrategicView-Plus.csproj.CoreCompileInputs.cache +++ b/obj/Debug/net472/StrategicView-Plus.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -2e4006519f508b84cea17598f3b73951cfa7c16c48b84e867d92bace76a313a9 +3cb9f30b2f7cdc911644f787aa9d0bd1dec3a38c35c65d0087457f82898e73da diff --git a/obj/Debug/net472/StrategicView-Plus.dll b/obj/Debug/net472/StrategicView-Plus.dll index 5fe3e74..1072fac 100644 Binary files a/obj/Debug/net472/StrategicView-Plus.dll and b/obj/Debug/net472/StrategicView-Plus.dll differ diff --git a/obj/Debug/net472/StrategicView-Plus.pdb b/obj/Debug/net472/StrategicView-Plus.pdb index dc8b09a..c5a8453 100644 Binary files a/obj/Debug/net472/StrategicView-Plus.pdb and b/obj/Debug/net472/StrategicView-Plus.pdb differ diff --git a/obj/StrategicView-Plus.csproj.nuget.g.props b/obj/StrategicView-Plus.csproj.nuget.g.props index 839c313..7c20b79 100644 --- a/obj/StrategicView-Plus.csproj.nuget.g.props +++ b/obj/StrategicView-Plus.csproj.nuget.g.props @@ -7,7 +7,7 @@ $(UserProfile)\.nuget\packages\ C:\Users\PC\.nuget\packages\;D:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 6.10.0 + 6.9.2 diff --git a/obj/project.nuget.cache b/obj/project.nuget.cache index dbb4124..99ba8be 100644 --- a/obj/project.nuget.cache +++ b/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "zU5GcbnMVE8=", + "dgSpecHash": "c2opvh31BiDg+DMTuh4qcKqCOMknou+psI/EB2sWw/WB0CwhntsEkJ0/nXAhoQMHMCuZl3iFWKs3U82rIyl/tQ==", "success": true, "projectFilePath": "D:\\Downloads\\Unity Modding\\StrategicMap-Plus\\StrategicView-Plus\\StrategicView-Plus.csproj", "expectedPackageFiles": [],