Skip to content

Add ui_LifebarOnlyDamaged toggle to hide full-HP lifebars#151

Open
M3RT1N99 wants to merge 1 commit intoFAForever:masterfrom
M3RT1N99:feature/lifebar-only-when-damaged
Open

Add ui_LifebarOnlyDamaged toggle to hide full-HP lifebars#151
M3RT1N99 wants to merge 1 commit intoFAForever:masterfrom
M3RT1N99:feature/lifebar-only-when-damaged

Conversation

@M3RT1N99
Copy link
Copy Markdown

@M3RT1N99 M3RT1N99 commented Apr 10, 2026

Summary

  • Adds a new ConVar ui_LifebarOnlyDamaged. When set to 1, lifebars only appear once a unit takes damage; full-HP units skip the lifebar batch entirely. Default 0 (vanilla behaviour).
  • Mirrors Warcraft 3's "Show Lifebars Only When Damaged" option.
  • Provides a noticeable FPS gain in dense crowds where most units are undamaged, since the per-frame lifebar batching cost scales with the unit count.

https://discord.com/channels/197033481883222026/1492294267492634674

Companion PR

The matching settings UI option lives in FAForever/fa#7085 — adds a HUD toggle that drives this ConVar via ConExecute. The two PRs should land together: this binary patch alone makes the ConVar available in the in-game console, the Lua PR alone is a no-op without this patch.

How it works

Hooks the call sub_85EED0 at 0x85C09F inside Moho::CWldSession::RenderStrategicIcons (the lifebar enqueue inside the per-unit loop). The 5-byte CALL is replaced with call LifebarFilter (same encoding length, no padding).

LifebarFilter is a small asm filter that:

  1. Saves caller state with pushad

  2. Reads the ui_LifebarOnlyDamaged flag — if 0, falls through to the original

  3. Reads the same HP fields the actual lifebar drawing function sub_85CD40 uses for the bar fill fraction:

    v18 = *(float **)eax0;        // entry[0] -> unit data ptr
    v19 = v18[26] / v18[27];      // current HP / max HP

    So we dereference entry[0] and read offsets 104 / 108 from the unit data pointer.

  4. If current HP < max HP → tail-calls sub_85EED0 (render)

  5. If current HP >= max HP → returns without enqueueing (suppress)

The custom calling convention of sub_85EED0 (eax = lifebar entry buffer pointer, ecx = batch vec pointer) is preserved on both paths via pushad/popad.

Files

  • hooks/LifebarFilter.hook — 5-byte CALL replacement at 0x85C09F
  • section/LifebarFilter.cpp — ConVar registration + asm filter
  • changelog.md — entry under Additions
  • Info.txt — documents sub_85EED0, sub_85CD40, and the entry HP layout

Test plan

  • Spawn many full-HP units in sandbox, enable ui_LifebarOnlyDamaged 1 — verify lifebars disappear for all of them
  • Damage a unit — verify its lifebar appears immediately
  • Heal it back to full HP — verify lifebar disappears again
  • Set ui_LifebarOnlyDamaged 0 — verify all lifebars come back (vanilla behaviour)
  • Verify no crash on unit death / unit creation while filter is active

🤖 Generated with Claude Code

When ui_LifebarOnlyDamaged is set to 1, the engine skips lifebar enqueue
for units at full HP, only showing lifebars once a unit has taken
damage. Default 0 (vanilla behaviour). Mirrors Warcraft 3's 'Show
Lifebars Only When Damaged' option.

Hooks the call sub_85EED0 at 0x85C09F inside
Moho::CWldSession::RenderStrategicIcons. The custom calling convention
of sub_85EED0 (eax = lifebar entry buffer ptr, ecx = batch vec) is
preserved by a small asm filter that either ret's (suppress) or
tail-calls the original (render).

The filter reads the same HP fields the actual lifebar drawing function
sub_85CD40 uses for the bar fill fraction:

  v18 = *(float **)eax0;        // entry[0] -> unit data ptr
  v19 = v18[26] / v18[27];      // current HP / max HP

so we read offsets 104/108 from the unit data pointer at entry[0] and
suppress when the two are equal.

Provides a noticeable FPS gain in dense crowds where most units are
undamaged, since the lifebar batch never receives the redundant entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

These changes implement a "show lifebars only when damaged" feature controlled by a new ui_LifebarOnlyDamaged ConVar. When enabled, lifebars display exclusively for units not at full health. Implementation spans documentation, hook registration at address 0x0085C09F, and inline x86 assembly that conditionally suppresses lifebar rendering based on unit health status.

Changes

Cohort / File(s) Summary
Documentation
Info.txt, changelog.md
Added descriptive comments documenting the lifebar filter patch, its function addresses, and the new ConVar's behavior.
Lifebar Filter Hook & Implementation
hooks/LifebarFilter.hook, section/LifebarFilter.cpp
Registered hook at 0x0085C09F to intercept RenderStrategicIcons calls. Implemented LifebarFilter function with x86 assembly that checks unit current HP against max HP; renders lifebars only when unit is damaged. Exposed ui_LifebarOnlyDamaged ConVar (backed by g_LifebarOnlyDamaged global) to control filter behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A filter most clever, with asm so fine,
Lifebars appear when damage draws a line,
Register-saved hops through conditional streams,
Only the wounded show health on our screens!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a UI toggle to hide lifebars for units at full health.
Linked Issues check ✅ Passed The PR does not reference any linked issues, but the description is self-contained and the objectives are clear without external issue context.
Out of Scope Changes check ✅ Passed All changes are directly related to the ui_LifebarOnlyDamaged feature implementation; no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering all essential aspects: a clear summary of the feature, behavior explanation, technical implementation details, file changes, and a detailed test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@section/LifebarFilter.cpp`:
- Around line 14-17: Update the comment block documenting HP offsets to match
the actual implementation and include/moho.h: change the mapped offsets so
UserEntity_base.mVarData @ +0x4C plus SSTIEntityVariableData.mHealth @ +0x18 ->
UserEntity +0x68, and SSTIEntityVariableData.mMaxHealth @ +0x1C -> UserEntity
+0x6C (this aligns with the reads in LifebarFilter where the code uses +0x68 and
+0x6C for health and max health). Mention include/moho.h as the corroborating
source and remove the incorrect +0x64/+0x68 mapping from the comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a9adef0b-ccff-41ab-abdf-1f8975dcf956

📥 Commits

Reviewing files that changed from the base of the PR and between 8da86fe and eaa8dd9.

📒 Files selected for processing (4)
  • Info.txt
  • changelog.md
  • hooks/LifebarFilter.hook
  • section/LifebarFilter.cpp

Comment on lines +14 to +17
// HP/MaxHealth offsets in Moho::UserEntity verified via IDA struct DB:
// UserEntity_base.mVarData @ +0x4C
// SSTIEntityVariableData.mHealth @ +0x18 -> UserEntity +0x64
// SSTIEntityVariableData.mMaxHealth @ +0x1C -> UserEntity +0x68
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the documented HP offsets.

These comments say mHealth / mMaxHealth resolve to UserEntity +0x64 / +0x68, but the implementation reads +0x68 / +0x6C at Lines 76-77, which also matches include/moho.h. Please correct the comment so future maintenance does not “fix” the asm to the wrong fields.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@section/LifebarFilter.cpp` around lines 14 - 17, Update the comment block
documenting HP offsets to match the actual implementation and include/moho.h:
change the mapped offsets so UserEntity_base.mVarData @ +0x4C plus
SSTIEntityVariableData.mHealth @ +0x18 -> UserEntity +0x68, and
SSTIEntityVariableData.mMaxHealth @ +0x1C -> UserEntity +0x6C (this aligns with
the reads in LifebarFilter where the code uses +0x68 and +0x6C for health and
max health). Mention include/moho.h as the corroborating source and remove the
incorrect +0x64/+0x68 mapping from the comment.

M3RT1N99 added a commit to M3RT1N99/fa that referenced this pull request Apr 10, 2026
Adds a new HUD setting that hides lifebars on units at full HP. Bars
only appear once a unit has taken damage. Provides a noticeable FPS
gain in dense crowds where most units are undamaged.

Requires the matching binary patch (FAForever/FA-Binary-Patches#151)
which exposes the ui_LifebarOnlyDamaged ConVar this option drives via
ConExecute. Without that patch the option toggle is a no-op (the
ConVar is unrecognised).

- lua/options/options.lua: new toggle in the HUD section
- lua/ui/help/tooltips.lua: tooltip entry options_gui_lifebar_only_damaged
- loc/US/strings_db.lua: OPTIONS_0287 / OPTIONS_0288 strings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant