fix(ui): restore retail skill training confirmation
Route untrained-skill promotion through DialogFactory with retail's exact prompt and stored skill/credit properties. Defer the character-sheet refresh until accept or reject completes while leaving trained-skill XP raises immediate. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
66bdae7a83
commit
85980fe13f
7 changed files with 341 additions and 14 deletions
|
|
@ -0,0 +1,79 @@
|
|||
# Retail skill-training confirmation
|
||||
|
||||
Date: 2026-07-13
|
||||
|
||||
## Scope
|
||||
|
||||
This note covers the confirmation shown when an untrained skill is promoted to
|
||||
trained by spending skill credits. It does **not** apply when experience is
|
||||
added to an already trained or specialized skill; those raises are sent
|
||||
immediately.
|
||||
|
||||
## Named retail sources
|
||||
|
||||
- `gmSkillUI::TrainSkillDialogCallback @ 0x0049C210`
|
||||
- `gmSkillUI::TrainSkill @ 0x0049C5F0`
|
||||
- `gmSkillUI::RaiseSelection @ 0x0049C8C0`
|
||||
- `DialogFactory::MakeCallbackDialogInCurrentUI @ 0x00478430`
|
||||
- literal `data_7A7864`: `Are you sure you want to spend %d credits to train %s?`
|
||||
|
||||
The dialog property keys are shared with the existing DialogFactory port:
|
||||
|
||||
```text
|
||||
0x8E dialog type (1 = ConfirmationDialog)
|
||||
0x92 confirmation result written by ConfirmationDialog
|
||||
0xC5 message StringInfo
|
||||
0x10000040 selected skill id
|
||||
0x10000041 skill-credit cost
|
||||
```
|
||||
|
||||
## Pseudocode
|
||||
|
||||
```text
|
||||
gmSkillUI.TrainSkill(playerQualities):
|
||||
if selectedSkill == 0:
|
||||
return false
|
||||
|
||||
skillName = SkillSystem.InqSkillName(selectedSkill)
|
||||
creditCost = GetCostToRaise(playerQualities)
|
||||
message = format(
|
||||
"Are you sure you want to spend %d credits to train %s?",
|
||||
creditCost,
|
||||
skillName)
|
||||
|
||||
data = PropertyCollection()
|
||||
data[0x8E] = 1 // ConfirmationDialog
|
||||
data[0xC5] = message
|
||||
data[0x10000040] = selectedSkill
|
||||
data[0x10000041] = creditCost
|
||||
DialogFactory.MakeCallbackDialogInCurrentUI(
|
||||
data,
|
||||
gmSkillUI.TrainSkillDialogCallback)
|
||||
return true
|
||||
|
||||
gmSkillUI.TrainSkillDialogCallback(data):
|
||||
if data.bool(0x92) is not true:
|
||||
return
|
||||
|
||||
skillId = data.uint(0x10000040)
|
||||
creditCost = data.uint(0x10000041)
|
||||
if skillId != 0 and creditCost != 0:
|
||||
CM_Train.Event_TrainSkillAdvancementClass(skillId, creditCost)
|
||||
```
|
||||
|
||||
`gmSkillUI::RaiseSelection` branches on advancement class. Untrained skills
|
||||
enter `TrainSkill`; trained/specialized skills call `CM_Train::Event_TrainSkill`
|
||||
directly with the XP cost. Therefore a confirmation on every skill XP raise
|
||||
would be a retail divergence.
|
||||
|
||||
## Cross-reference
|
||||
|
||||
- ACE `GameActionTrainSkill.Handle` reads exactly two fields, skill id and
|
||||
credits spent, then calls `HandleActionTrainSkill`.
|
||||
- Chorizite's generated native client bindings expose
|
||||
`CM_Train::Event_TrainSkillAdvancementClass(UInt32 skill, UInt32 credits)` and
|
||||
separately expose `CM_Train::Event_TrainSkill(UInt32 skill, UInt32 xp)`.
|
||||
|
||||
These corroborate the callback's two stored properties and the distinction
|
||||
between training a skill and adding XP to a trained skill. The named retail
|
||||
client remains the behavioral oracle for when the dialog appears.
|
||||
Loading…
Add table
Add a link
Reference in a new issue