Resolve authored StringInfo labels from local.dat, port gmCombatUI's runtime option captions and checkbox widgets, and bind horizontal scrollbar media by retail structural roles so the green power jewel is a thumb instead of a tiled track. Persist the three combat options and make Auto Target govern target acquisition. Co-Authored-By: Codex <codex@openai.com>
6.3 KiB
Retail basic combat bar — port pseudocode
Sources
- Named retail decomp:
gmCombatUI::RecvNotice_AttackHeightChanged0x004CC080gmCombatUI::RecvNotice_SetPowerbarLevel0x004CC0E0gmCombatUI::RecvNotice_DesiredAttackPowerChanged0x004CC110gmCombatUI::PostInit0x004CC1A0gmCombatUI::ListenToElementMessage0x004CC430gmCombatUI::RecvNotice_SetCombatMode0x004CC620ClientCombatSystem::Begin0x0056A460ClientCombatSystem::StartPowerBarBuild0x0056ADB0ClientCombatSystem::GetPowerBarLevel0x0056ADE0ClientCombatSystem::StartAttackRequest0x0056C040ClientCombatSystem::EndAttackRequest0x0056C0E0ClientCombatSystem::UseTime0x0056C1F0ClientCombatSystem::HandleAttackDoneEvent0x0056C500ClientCombatSystem::SetRequestedAttackHeight0x0056C8F0UIOption_Checkbox::UIOption_Checkbox0x00486A50UIOption_Checkbox::ListenToElementMessage0x004869A0compute_str_hash0x00413110
- Matching retail executable disassembly (
acclient.exev11.4186 + PDB):ATTACK_POWERUP_TIME = 1.0seconds andDUAL_WIELD_POWERUP_TIME = 0.8seconds. The named pseudo-C's x87 expression had incorrectly collapsed the division operands. - LayoutDesc
0x21000073(gmCombatUI), cross-checked against the retail UI layout dump. Root is 610×90; basic page is0x1000005C, desired-power scrollbar is0x1000004F, embedded meter is0x10000050, and the high/medium/low buttons are0x10000057/58/59. - Interpretation cross-checks: ACE combat request/power fields and the
holtburger
DesiredAttackProfile -> attack_requestwire path summarized indocs/research/deepdives/r02-combat-system.md.
Initial state
combatMode = NonCombat
buildInProgress = false
attackRequestInProgress = false
attackServerResponsePending = false
requestedAttackPower = 0
requestedAttackHeight = Medium
desiredUiPower = 0.5
repeatAttacking = false
Panel visibility
when combat mode changes:
if AdvancedCombatUI:
hide gmCombatUI
else if mode == Melee:
set root state Melee (0x10000003)
show gmCombatUI
else if mode == Missile:
set root state Missile (0x10000004)
show gmCombatUI
else:
hide gmCombatUI
The current acdream port binds the basic page because the persisted gameplay
default has AdvancedCombatUI = false. The separate advanced-combat surface is
still tracked by divergence row AP-110.
Press / charge / release
SetRequestedAttackHeight(height):
changed = requestedAttackHeight != height
requestedAttackHeight = height
if changed OR no attack request is active:
StartAttackRequest()
StartAttackRequest():
target = current attack target
if target is not attackable: report no target and stop
attackRequestInProgress = true
requestedAttackPower = 1
stop jump and movement
currentBuildIsAutomatic = false
if player is ready and no build is active:
StartPowerBarBuild()
StartPowerBarBuild():
buildInProgress = true
buildStartTime = currentTime
displayedPower = 0
GetPowerBarLevel():
if no build: return 0
duration = current stance is DualWieldCombat ? 0.8 seconds : 1.0 second
return clamp((currentTime - buildStartTime) / duration, 0, 1)
EndAttackRequest(height, explicitPower = -1):
if no attack request: stop
attackRequestInProgress = false
current = GetPowerBarLevel()
requestedAttackPower = explicitPower >= 0
? explicitPower
: min(desiredUiPower, current)
if a prior attack is awaiting the server:
queue this power for AttackDone
else if desiredUiPower <= current OR repeat attack is active:
ExecuteAttack(height)
otherwise:
UseTime commits on its next eligible tick at requestedAttackPower
UseTime():
if a build is active:
current = GetPowerBarLevel()
publish current to meter
if request was released AND current >= requestedAttackPower:
publish min(requestedAttackPower, current)
if build is not automatic: ExecuteAttack(requestedHeight)
else stop automatic build
UI messages
mouse-down high/medium/low:
SetRequestedAttackHeight(High/Medium/Low)
mouse-up high/medium/low:
EndAttackRequest(the height, -1)
desired-power slider changed with integer position 0..1000:
desiredUiPower = clamp(position * 0.001, 0, 1)
attack power increase/decrease action:
move desiredUiPower by one sixth, clamped to 0..1
Both keyboard and retained buttons therefore use one transition-based attack controller. A one-shot click/keypress is not sufficient: down begins charging, up ends the request.
Authored labels and option checkboxes
PostInit():
repeat = child 0x10000053 as UIOption_Checkbox
repeat.playerOption = AutoRepeatAttack
repeat.label = StringInfo(hash("ID_CombatPanelOption_AutoRepeatAttack"))
autoTarget = child 0x10000054 as UIOption_Checkbox
autoTarget.playerOption = AutoTarget
autoTarget.label = StringInfo(hash("ID_CombatPanelOption_AutoTarget"))
keepInView = child 0x10000055 as UIOption_Checkbox
keepInView.playerOption = ViewCombatTarget
keepInView.label = StringInfo(hash("ID_CombatPanelOption_ViewCombatTarget"))
The first two labels are deliberately not authored in LayoutDesc. They are
assigned by gmCombatUI::PostInit, so a layout-only renderer must perform that
controller binding. Ordinary Type-12 captions and High/Medium/Low use StringInfo
property 0x17 and resolve through local.dat table 0x23000001.
UIOption_Checkbox is a toggle UIElement_Button. Its visible 13x13 face is
the authored child 0x10000328; the label is embedded in the option object and
starts after that face. Mouse release toggles attribute 0x0E and immediately
applies the associated player option.
Horizontal scrollbar media roles
track = scrollbar DirectState image
thumb = structural child element 1
live power fill = nested meter child 0x10000050 / image child 2
These roles are identified by authored element ids, never by resolved width.
The combat thumb is a 12x14 green jewel; inheritance reflow can make its
effective child geometry wide enough that a width heuristic incorrectly tiles
the jewel as the background. The base track is 0x060074CA, thumb
0x06001923, and live red fill 0x06001200.