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>
2.6 KiB
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 @ 0x0049C210gmSkillUI::TrainSkill @ 0x0049C5F0gmSkillUI::RaiseSelection @ 0x0049C8C0DialogFactory::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:
0x8E dialog type (1 = ConfirmationDialog)
0x92 confirmation result written by ConfirmationDialog
0xC5 message StringInfo
0x10000040 selected skill id
0x10000041 skill-credit cost
Pseudocode
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.Handlereads exactly two fields, skill id and credits spent, then callsHandleActionTrainSkill. - Chorizite's generated native client bindings expose
CM_Train::Event_TrainSkillAdvancementClass(UInt32 skill, UInt32 credits)and separately exposeCM_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.