mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-07-31 01:41:14 -04:00
renderBuffBar is ~400 lines and the layout maths was buried in the middle of it, which is exactly where a silent bug lives: the row does not wrap, so it has a hard capacity, and it has to stay clear of the minimap at every resolution and scale. The arithmetic moves to buff_bar_layout.hpp as a pure function and combat_ui.cpp calls it, so the tested maths is the maths that runs. 408 assertions sweep resolutions from 1280x720 to 4K and scales from 0.75x to 1.5x, checking the row never overlaps the minimap, never runs off screen, drops overflow rather than wrapping, and always gives weapon enchants their slots before auras.
93 lines
4.1 KiB
C++
93 lines
4.1 KiB
C++
#pragma once
|
|
|
|
// Pure layout arithmetic for the buff bar, kept out of the ImGui code so it can be
|
|
// tested. The bar is a single row along the top of the screen, right of centre: it is
|
|
// right-aligned to end just left of the minimap and grows leftwards as auras are
|
|
// gained. Nothing wraps to a second row, so the row has a hard capacity and the
|
|
// arithmetic that decides it is worth pinning down.
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
|
|
namespace wowee {
|
|
namespace ui {
|
|
|
|
struct BuffBarLayout {
|
|
float iconSize = 0.0f; // per-icon edge length, in pixels
|
|
float iconSpacing = 0.0f; // gap between icons
|
|
float barWidth = 0.0f; // window width
|
|
float barX = 0.0f; // window left edge, in screen pixels
|
|
float barY = 0.0f; // window top edge
|
|
int maxIcons = 0; // how many icons fit on the row at all
|
|
int auraShown = 0; // auras that made the cut
|
|
int enchantShown = 0; // weapon enchants that made the cut
|
|
int iconCount = 0; // auraShown + enchantShown
|
|
};
|
|
|
|
struct BuffBarMetrics {
|
|
// 25% larger than the original 32px icons, at the 1080p reference height.
|
|
static constexpr float kBaseIconSize = 40.0f;
|
|
static constexpr float kBaseIconSpacing = 2.0f;
|
|
static constexpr float kWindowPadding = 16.0f; // 8px each side
|
|
static constexpr float kReferenceHeight = 1080.0f;
|
|
// The minimap is 200x200 at a 10px margin, so its left edge sits 210px in from the
|
|
// right of the screen. The row must stop before it.
|
|
static constexpr float kMinimapLeftEdge = 210.0f;
|
|
static constexpr float kRightGap = 10.0f;
|
|
static constexpr float kScreenMargin = 10.0f;
|
|
static constexpr float kTopMargin = 10.0f;
|
|
// Floor: the Dismiss Pet button needs somewhere to sit even with no auras.
|
|
static constexpr float kMinBarWidth = 140.0f;
|
|
// Bounds on the automatic resolution scaling.
|
|
static constexpr float kMinAutoScale = 0.75f;
|
|
static constexpr float kMaxAutoScale = 2.0f;
|
|
};
|
|
|
|
/// Icons track the window height so the bar keeps its proportions at any resolution,
|
|
/// with the user's Buff Bar Scale setting layered on top.
|
|
inline float buffBarScale(float screenH, float userScale) {
|
|
const float autoScale = std::clamp(screenH / BuffBarMetrics::kReferenceHeight,
|
|
BuffBarMetrics::kMinAutoScale,
|
|
BuffBarMetrics::kMaxAutoScale);
|
|
return autoScale * userScale;
|
|
}
|
|
|
|
/// Lay out a single row of `auraCount` auras plus `enchantCount` weapon enchants.
|
|
///
|
|
/// Weapon enchants claim their slots first: a wall of buffs must not push a sharpening
|
|
/// stone off the row. Anything that still does not fit is dropped rather than wrapped.
|
|
inline BuffBarLayout computeBuffBarLayout(float screenW, float screenH, float userScale,
|
|
int auraCount, int enchantCount) {
|
|
using M = BuffBarMetrics;
|
|
|
|
BuffBarLayout out;
|
|
const float scale = buffBarScale(screenH, userScale);
|
|
out.iconSize = M::kBaseIconSize * scale;
|
|
out.iconSpacing = M::kBaseIconSpacing * scale;
|
|
out.barY = M::kTopMargin;
|
|
|
|
// Space between the left edge of the screen and the left edge of the minimap.
|
|
const float availableW = screenW - M::kMinimapLeftEdge - M::kRightGap - M::kScreenMargin;
|
|
out.maxIcons = std::max(1, static_cast<int>(
|
|
(availableW - M::kWindowPadding + out.iconSpacing) / (out.iconSize + out.iconSpacing)));
|
|
|
|
out.enchantShown = std::clamp(enchantCount, 0, out.maxIcons);
|
|
out.auraShown = std::clamp(auraCount, 0, out.maxIcons - out.enchantShown);
|
|
out.iconCount = out.auraShown + out.enchantShown;
|
|
|
|
out.barWidth = M::kWindowPadding;
|
|
if (out.iconCount > 0) {
|
|
out.barWidth += out.iconCount * out.iconSize +
|
|
(out.iconCount - 1) * out.iconSpacing;
|
|
}
|
|
out.barWidth = std::max(out.barWidth, M::kMinBarWidth * scale);
|
|
|
|
// Right-align against the minimap's left edge, but never run off screen.
|
|
out.barX = std::max(M::kScreenMargin,
|
|
screenW - M::kMinimapLeftEdge - M::kRightGap - out.barWidth);
|
|
return out;
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace wowee
|