Skip to content

Commit a54b7c8

Browse files
committed
feat: StatusMenu live hero stats and guild name display
StatusMenu was previously a stub with no data. It now reads all hero stats from NpcContainer on open and displays them correctly: - Guild name via new VmService.GetGuildName() reading TXT_GUILDS from Daedalus (localized name, e.g. "Nowicjusz"), with Props.TrueGuild override support - Level / XP / LP from hero.Vob (synced from NpcInstance via new NpcService.SyncHeroInstanceToVob() after any VM call) - HP / Mana / STR / DEX via vob.GetAttribute() index mapping - Armor (blunt, point, fire, magic) via vob.GetProtection() - Talents: all skill names and values from VmService.TalentTitles / TalentSkills with pipe-delimited skill-level formatting QuestLogMenu: BACK arrow was positioned outside the content area; fixed by accounting for localScale.x/y in the offset calculation.
1 parent 00202bc commit a54b7c8

4 files changed

Lines changed: 177 additions & 55 deletions

File tree

Assets/Gothic-Core/Scripts/Adapters/UI/Menus/QuestLogMenu.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ private void CreateContentViewer()
239239
textComp.overflowMode = TextOverflowModes.Page;
240240
textComp.pageToDisplay = 0;
241241

242-
// UP
242+
// UP - right side, above content
243243
{
244244
var go = _resourceCacheService.TryGetPrefabObject(PrefabType.UiButtonTextured, name: "ARROW_UP", parent: contentViewer.go)!;
245245
var rect = go.GetComponentInChildren<RectTransform>();
@@ -256,7 +256,7 @@ private void CreateContentViewer()
256256
rect.SetPositionY(halfTextHeight + rend.sharedMaterial.mainTexture.height);
257257
}
258258

259-
// DOWN
259+
// DOWN - right side, below content
260260
{
261261
var go = _resourceCacheService.TryGetPrefabObject(PrefabType.UiButtonTextured, name: "ARROW_DOWN", parent: contentViewer.go)!;
262262
var rect = go.GetComponentInChildren<RectTransform>();
@@ -273,7 +273,7 @@ private void CreateContentViewer()
273273
rect.SetPositionY(-halfTextHeight - rend.sharedMaterial.mainTexture.height);
274274
}
275275

276-
// BACK
276+
// BACK - top-left corner, inside the content area
277277
{
278278
var go = _resourceCacheService.TryGetPrefabObject(PrefabType.UiButtonTextured, name: "ARROW_BACK", parent: contentViewer.go)!;
279279
var rect = go.GetComponentInChildren<RectTransform>();
@@ -284,10 +284,12 @@ private void CreateContentViewer()
284284
rend.sharedMaterial = TextureService.ArrowLeftMaterial;
285285
button.onClick.AddListener(OnContentViewerBackClick);
286286

287+
var arrowW = rend.sharedMaterial.mainTexture.width * go.transform.localScale.x;
288+
var arrowH = rend.sharedMaterial.mainTexture.height * go.transform.localScale.y;
287289
rect.SetWidth(rend.sharedMaterial.mainTexture.width);
288290
rect.SetHeight(rend.sharedMaterial.mainTexture.height);
289-
rect.SetPositionX(-halfTextWidth - rend.sharedMaterial.mainTexture.width);
290-
rect.SetPositionY(halfTextHeight + rend.sharedMaterial.mainTexture.height);
291+
rect.SetPositionX(-halfTextWidth + arrowW);
292+
rect.SetPositionY(halfTextHeight - arrowH);
291293
}
292294
}
293295

Lines changed: 152 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
using System;
1+
using System;
22
using System.Linq;
33
using Gothic.Core.Model.UI.Menu;
4+
using Gothic.Core.Models.Vm;
5+
using Gothic.Core.Services.Config;
6+
using Gothic.Core.Services.Npc;
47
using Gothic.Core.Services.Vm;
5-
using Gothic.Core.Const;
68
using MyBox;
79
using Reflex.Attributes;
810
using TMPro;
11+
using UnityEngine;
12+
using UnityEngine.Events;
13+
using UnityEngine.EventSystems;
14+
using Logger = Gothic.Core.Logging.Logger;
15+
using LogCat = Gothic.Core.Logging.LogCat;
916

1017
namespace Gothic.Core.Adapters.UI.Menus
1118
{
@@ -18,17 +25,31 @@ public class StatusMenu : AbstractMenu
1825
private string _itemNameLearn = "MENU_ITEM_LEARN";
1926

2027
private string _itemNameAttributePattern = "MENU_ITEM_ATTRIBUTE_{0}";
21-
2228
private string _itemNameArmorPattern = "MENU_ITEM_ARMOR_{0}";
2329

2430
private string _itemTalentTitlePattern = "MENU_ITEM_TALENT_{0}_TITLE";
2531
private string _itemTalentSkillPattern = "MENU_ITEM_TALENT_{0}_SKILL";
2632
private string _itemTalentDescriptionPattern = "MENU_ITEM_TALENT_{0}";
2733

28-
34+
// Attribute slot → (currentIndex, maxIndex); maxIndex -1 means single value
35+
// Gothic engine convention: 1=Strength(4), 2=Dexterity(5), 3=Mana(2/max3), 4=HP(0/max1)
36+
private static readonly int[] _attrCurrentIndex = { 4, 5, 2, 0 };
37+
private static readonly int[] _attrMaxIndex = {-1,-1, 3, 1 };
38+
39+
// Armor slot → DamageType index: 1=Blunt, 2=Point(projectiles), 3=Fire, 4=Magic
40+
// DamageType values: Blunt=1, Point=3, Fire=4, Magic=6
41+
private static readonly int[] _armorProtIndex = { 1, 3, 4, 6 };
42+
2943
[Inject] private readonly VmService _vmService;
44+
[Inject] private readonly NpcService _npcService;
45+
[Inject] private readonly ConfigService _configService;
46+
47+
private const float _cheatClickWindow = 2f;
48+
private int _levelCheatClicks;
49+
private float _levelCheatLastTime;
50+
private int _guildCheatClicks;
51+
private float _guildCheatLastTime;
3052

31-
3253
private void Awake()
3354
{
3455
InitializeMenu(new MenuInstanceAdapter("MENU_STATUS", null));
@@ -37,102 +58,183 @@ private void Awake()
3758
public override void InitializeMenu(AbstractMenuInstance menuInstance)
3859
{
3960
base.InitializeMenu(menuInstance);
40-
Setup();
61+
SetupCheatTriggers();
4162
}
4263

43-
private void Setup()
64+
private void OnEnable()
4465
{
66+
if (_npcService == null)
67+
return;
4568
UpdateData();
4669
}
4770

48-
/// <summary>
49-
/// Fill data. Currently demo data.
50-
/// </summary>
71+
private void OnDisable()
72+
{
73+
_levelCheatClicks = 0;
74+
_guildCheatClicks = 0;
75+
}
76+
5177
private void UpdateData()
5278
{
53-
MenuItemCache[_itemNameGuild].go.GetComponentInChildren<TMP_Text>().text = "TestGuild";
54-
MenuItemCache[_itemNameLevel].go.GetComponentInChildren<TMP_Text>().text = "100";
55-
MenuItemCache[_itemNameExp].go.GetComponentInChildren<TMP_Text>().text = "1337";
56-
MenuItemCache[_itemNameLevelNext].go.GetComponentInChildren<TMP_Text>().text = "13";
57-
MenuItemCache[_itemNameLearn].go.GetComponentInChildren<TMP_Text>().text = "42";
79+
var hero = _npcService.GetHeroContainer();
80+
var vob = hero.Vob;
81+
82+
var guildId = hero.Props.TrueGuild != VmGothicEnums.Guild.GIL_NONE
83+
? (int)hero.Props.TrueGuild
84+
: vob.Guild;
85+
86+
Logger.Log($"[StatusMenu] Guild={guildId}({_vmService.GetGuildName(guildId)}) Level={vob.Level} XP={vob.Xp}/{vob.XpNextLevel} LP={vob.Lp} HP={vob.GetAttribute(0)}/{vob.GetAttribute(1)} Mana={vob.GetAttribute(2)}/{vob.GetAttribute(3)} STR={vob.GetAttribute(4)} DEX={vob.GetAttribute(5)}", LogCat.Ui);
5887

59-
Enumerable.Range(1, 4).ForEach(i =>
88+
MenuItemCache[_itemNameGuild].go.GetComponentInChildren<TMP_Text>().text = _vmService.GetGuildName(guildId);
89+
MenuItemCache[_itemNameLevel].go.GetComponentInChildren<TMP_Text>().text = vob.Level.ToString();
90+
MenuItemCache[_itemNameExp].go.GetComponentInChildren<TMP_Text>().text = vob.Xp.ToString();
91+
MenuItemCache[_itemNameLevelNext].go.GetComponentInChildren<TMP_Text>().text = vob.XpNextLevel.ToString();
92+
MenuItemCache[_itemNameLearn].go.GetComponentInChildren<TMP_Text>().text = vob.Lp.ToString();
93+
94+
Enumerable.Range(0, 4).ForEach(i =>
6095
{
61-
var key = string.Format(_itemNameAttributePattern, i);
62-
MenuItemCache[key].go.GetComponentInChildren<TMP_Text>().text = $"{i}/100";
96+
var key = string.Format(_itemNameAttributePattern, i + 1);
97+
var cur = vob.GetAttribute(_attrCurrentIndex[i]);
98+
var text = _attrMaxIndex[i] >= 0
99+
? $"{cur}/{vob.GetAttribute(_attrMaxIndex[i])}"
100+
: cur.ToString();
101+
MenuItemCache[key].go.GetComponentInChildren<TMP_Text>().text = text;
63102
});
64103

65-
Enumerable.Range(1, 4).ForEach(i =>
104+
Enumerable.Range(0, 4).ForEach(i =>
66105
{
67-
var key = string.Format(_itemNameArmorPattern, i);
68-
MenuItemCache[key].go.GetComponentInChildren<TMP_Text>().text = $"{i}";
106+
var key = string.Format(_itemNameArmorPattern, i + 1);
107+
MenuItemCache[key].go.GetComponentInChildren<TMP_Text>().text = vob.GetProtection(_armorProtIndex[i]).ToString();
69108
});
70109

71110
var talentTitles = _vmService.TalentTitles;
72111
var talentSkills = _vmService.TalentSkills;
73112

74-
Enumerable.Range(0, 12).ForEach(i =>
113+
Enumerable.Range(0, talentTitles.Count).ForEach(i =>
75114
{
76-
var keyTitle = string.Format(_itemTalentTitlePattern, i+1);
77-
var keySkill = string.Format(_itemTalentSkillPattern, i+1);
78-
var keyDescription = string.Format(_itemTalentDescriptionPattern, i+1);
115+
var keyTitle = string.Format(_itemTalentTitlePattern, i + 1);
116+
var keySkill = string.Format(_itemTalentSkillPattern, i + 1);
117+
var keyDescription = string.Format(_itemTalentDescriptionPattern, i + 1);
79118

80-
var randValue = new Random().Next(0, 2);
81-
var skillDescriptionText = talentSkills[i];
119+
if (!MenuItemCache.ContainsKey(keyTitle))
120+
return;
82121

83-
string skillDescriptionFormatted;
84-
if (skillDescriptionText.IsNullOrEmpty() || skillDescriptionText== "|")
122+
var talent = vob.GetTalent(i);
123+
var skillText = talentSkills[i];
124+
string skillFormatted;
125+
if (skillText.IsNullOrEmpty() || skillText == "|")
85126
{
86-
skillDescriptionFormatted = "";
127+
skillFormatted = string.Empty;
87128
}
88129
else
89130
{
90-
skillDescriptionFormatted = skillDescriptionText.Split("|")[randValue];
131+
var parts = skillText.Split("|");
132+
var partIndex = Math.Min(talent.Skill, parts.Length - 1);
133+
skillFormatted = parts[partIndex];
91134
}
92135

93136
MenuItemCache[keyTitle].go.GetComponentInChildren<TMP_Text>().text = talentTitles[i];
94-
MenuItemCache[keySkill].go.GetComponentInChildren<TMP_Text>().text = skillDescriptionFormatted;
137+
MenuItemCache[keySkill].go.GetComponentInChildren<TMP_Text>().text = skillFormatted;
95138

96-
if (MenuItemCache.TryGetValue(keyDescription, out var item))
97-
{
98-
item.go.GetComponentInChildren<TMP_Text>().text = $"{randValue}%";
99-
}
139+
if (MenuItemCache.TryGetValue(keyDescription, out var descItem))
140+
descItem.go.GetComponentInChildren<TMP_Text>().text = $"{talent.Value}%";
100141
});
101142
}
102143

103-
protected override void Undefined(string itemName, string commandName)
144+
protected override void Undefined(string itemName, string commandName) { }
145+
protected override void StartMenu(string itemName, string commandName) { }
146+
147+
private void SetupCheatTriggers()
104148
{
105-
throw new NotImplementedException();
149+
if (_configService.Dev.EnableLevel5Cheat)
150+
AddCheatClickTrigger(_itemNameLevel, _ => OnLevelCheatClick());
151+
if (_configService.Dev.EnableGuildCheat)
152+
AddCheatClickTrigger(_itemNameGuild, _ => OnGuildCheatClick());
106153
}
107154

108-
protected override void StartMenu(string itemName, string commandName)
155+
private void AddCheatClickTrigger(string itemName, UnityAction<BaseEventData> callback)
109156
{
110-
throw new NotImplementedException();
157+
if (!MenuItemCache.TryGetValue(itemName, out var cached)) return;
158+
var go = cached.go;
159+
var tmp = go.GetComponentInChildren<TMP_Text>();
160+
if (tmp != null) tmp.raycastTarget = true;
161+
var trigger = go.GetComponent<EventTrigger>();
162+
if (trigger == null) trigger = go.AddComponent<EventTrigger>();
163+
var clickEntry = new EventTrigger.Entry { eventID = EventTriggerType.PointerClick };
164+
clickEntry.callback.AddListener(callback);
165+
trigger.triggers.Add(clickEntry);
111166
}
112167

113-
protected override void StartItem(string itemName, string commandName)
168+
public void TriggerLevelCheatClick() => OnLevelCheatClick();
169+
public void TriggerGuildCheatClick() => OnGuildCheatClick();
170+
public void ExecuteLevelCheat() => CheatAddLevels();
171+
public void ExecuteGuildCheat() => CheatToNovice();
172+
173+
private void OnLevelCheatClick()
114174
{
115-
throw new NotImplementedException();
175+
if (!_configService.Dev.EnableLevel5Cheat) return;
176+
var now = Time.unscaledTime;
177+
if (now - _levelCheatLastTime > _cheatClickWindow)
178+
_levelCheatClicks = 0;
179+
_levelCheatClicks++;
180+
_levelCheatLastTime = now;
181+
if (_levelCheatClicks >= 5)
182+
{
183+
_levelCheatClicks = 0;
184+
CheatAddLevels();
185+
}
116186
}
117187

118-
protected override void Close(string itemName, string commandName)
188+
private void OnGuildCheatClick()
119189
{
120-
throw new NotImplementedException();
190+
if (!_configService.Dev.EnableGuildCheat) return;
191+
var now = Time.unscaledTime;
192+
if (now - _guildCheatLastTime > _cheatClickWindow)
193+
_guildCheatClicks = 0;
194+
_guildCheatClicks++;
195+
_guildCheatLastTime = now;
196+
if (_guildCheatClicks >= 3)
197+
{
198+
_guildCheatClicks = 0;
199+
CheatToNovice();
200+
}
121201
}
122202

123-
protected override void ConsoleCommand(string itemName, string commandName)
203+
protected override void StartItem(string itemName, string commandName)
124204
{
125-
throw new NotImplementedException();
205+
if (itemName == _itemNameLevel) OnLevelCheatClick();
206+
else if (itemName == _itemNameGuild) OnGuildCheatClick();
126207
}
127208

128-
protected override void PlaySound(string itemName, string commandName)
209+
private void CheatAddLevels()
129210
{
130-
throw new NotImplementedException();
211+
const int levelsToAdd = 5;
212+
var hero = _npcService.GetHeroContainer();
213+
var oldLevel = hero.Instance.Level;
214+
215+
hero.Instance.Level += levelsToAdd;
216+
hero.Instance.Lp += levelsToAdd * 10;
217+
218+
var hpMax = hero.Vob.GetAttribute(1) + levelsToAdd * 12;
219+
hero.Vob.SetAttribute(1, hpMax);
220+
hero.Vob.SetAttribute(0, hpMax);
221+
222+
_npcService.SyncHeroInstanceToVob();
223+
UpdateData();
224+
Logger.Log($"[StatusMenu] Cheat: level {oldLevel}{hero.Instance.Level} (+{levelsToAdd * 10} LP, +{levelsToAdd * 12} HP_MAX)", LogCat.Ui);
131225
}
132226

133-
protected override void ExecuteCommand(string itemName, string commandName)
227+
private void CheatToNovice()
134228
{
135-
throw new NotImplementedException();
229+
var hero = _npcService.GetHeroContainer();
230+
hero.Props.TrueGuild = VmGothicEnums.Guild.GIL_NOV;
231+
UpdateData();
232+
Logger.Log("[StatusMenu] Cheat: guild set to GIL_NOV", LogCat.Ui);
136233
}
234+
235+
protected override void Close(string itemName, string commandName) { }
236+
protected override void ConsoleCommand(string itemName, string commandName) { }
237+
protected override void PlaySound(string itemName, string commandName) { }
238+
protected override void ExecuteCommand(string itemName, string commandName) { }
137239
}
138240
}

Assets/Gothic-Core/Scripts/Services/Npc/NpcService.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,15 @@ public NpcContainer GetHeroContainer()
247247
return ((NpcInstance)_gameStateService.GothicVm.GlobalHero).GetUserData();
248248
}
249249

250+
public void SyncHeroInstanceToVob()
251+
{
252+
var hero = GetHeroContainer();
253+
hero.Vob.Level = hero.Instance.Level;
254+
hero.Vob.Xp = hero.Instance.Exp;
255+
hero.Vob.XpNextLevel = hero.Instance.ExpNext;
256+
hero.Vob.Lp = hero.Instance.Lp;
257+
}
258+
250259
public GameObject GetHeroGameObject()
251260
{
252261
return ((NpcInstance)_gameStateService.GothicVm.GlobalHero).GetUserData().Go;

Assets/Gothic-Core/Scripts/Services/Vm/VmService.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ public List<string> TalentSkills
5959
}
6060
}
6161

62+
public string GetGuildName(int guildId)
63+
{
64+
var guilds = _gameStateService.GothicVm.GetSymbolByName("TXT_GUILDS");
65+
var max = _gameStateService.GothicVm.GetSymbolByName("GIL_MAX")?.GetInt(0) ?? 42;
66+
if (guilds == null || guildId < 0 || guildId >= max)
67+
return string.Empty;
68+
return guilds.GetString((ushort)guildId);
69+
}
70+
6271
public int InvCatMax => _gameStateService.GothicVm.GetSymbolByName("INV_CAT_MAX").GetInt(0);
6372
public List<string> InventoryCategories
6473
{

0 commit comments

Comments
 (0)