mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-11 12:27:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba4520493a | ||
|
|
dc80ade21b |
@@ -14,6 +14,7 @@ Util\Constants.lua
|
||||
Util\String.lua
|
||||
Util\TableUtil.lua
|
||||
Util\MathUtil.lua
|
||||
Util\PixelUtil.lua
|
||||
Util\FrameUtil.lua
|
||||
Util\FormattingUtil.lua
|
||||
Util\TimeUtil.lua
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
-- Backport of FrameXML/PixelUtil.lua to vanilla 1.12 / Lua 5.0.
|
||||
--
|
||||
-- PixelUtil snaps frame sizes/points to whole physical pixels so
|
||||
-- borders and textures stay crisp at fractional UI scales. The table
|
||||
-- ports verbatim — its only math deps (Round, Lerp,
|
||||
-- ClampedPercentageBetween) all live in MathUtil, which loads first.
|
||||
--
|
||||
-- The one piece vanilla lacks is the native `GetPhysicalScreenSize()`
|
||||
-- (added in 7.0). The ClassicAPI DLL provides it (see
|
||||
-- src/screen/Info.cpp) by reading the engine's `gxResolution` CVar, so
|
||||
-- it's a real global registered at engine boot — no Lua polyfill
|
||||
-- needed here.
|
||||
|
||||
PixelUtil = {};
|
||||
|
||||
function PixelUtil.GetPixelToUIUnitFactor()
|
||||
local physicalWidth, physicalHeight = GetPhysicalScreenSize();
|
||||
return 768.0 / physicalHeight;
|
||||
end
|
||||
|
||||
function PixelUtil.GetNearestPixelSize(uiUnitSize, layoutScale, minPixels)
|
||||
if uiUnitSize == 0 and (not minPixels or minPixels == 0) then
|
||||
return 0;
|
||||
end
|
||||
|
||||
local uiUnitFactor = PixelUtil.GetPixelToUIUnitFactor();
|
||||
local numPixels = Round((uiUnitSize * layoutScale) / uiUnitFactor);
|
||||
if minPixels then
|
||||
if uiUnitSize < 0.0 then
|
||||
if numPixels > -minPixels then
|
||||
numPixels = -minPixels;
|
||||
end
|
||||
else
|
||||
if numPixels < minPixels then
|
||||
numPixels = minPixels;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return numPixels * uiUnitFactor / layoutScale;
|
||||
end
|
||||
|
||||
function PixelUtil.ConvertPixelsToUI(desiredPixels, layoutScale)
|
||||
return PixelUtil.GetNearestPixelSize(desiredPixels, layoutScale);
|
||||
end
|
||||
|
||||
function PixelUtil.ConvertPixelsToUIForRegion(desiredPixels, region)
|
||||
return PixelUtil.GetNearestPixelSize(desiredPixels, region:GetEffectiveScale());
|
||||
end
|
||||
|
||||
function PixelUtil.SetWidth(region, width, minPixels)
|
||||
region:SetWidth(PixelUtil.GetNearestPixelSize(width, region:GetEffectiveScale(), minPixels));
|
||||
end
|
||||
|
||||
function PixelUtil.SetHeight(region, height, minPixels)
|
||||
region:SetHeight(PixelUtil.GetNearestPixelSize(height, region:GetEffectiveScale(), minPixels));
|
||||
end
|
||||
|
||||
function PixelUtil.SetSize(region, width, height, minWidthPixels, minHeightPixels)
|
||||
PixelUtil.SetWidth(region, width, minWidthPixels);
|
||||
PixelUtil.SetHeight(region, height, minHeightPixels);
|
||||
end
|
||||
|
||||
function PixelUtil.SetPoint(region, point, relativeTo, relativePoint, offsetX, offsetY, minOffsetXPixels, minOffsetYPixels)
|
||||
region:SetPoint(point, relativeTo, relativePoint,
|
||||
PixelUtil.GetNearestPixelSize(offsetX, region:GetEffectiveScale(), minOffsetXPixels),
|
||||
PixelUtil.GetNearestPixelSize(offsetY, region:GetEffectiveScale(), minOffsetYPixels)
|
||||
);
|
||||
end
|
||||
|
||||
function PixelUtil.SetStatusBarValue(statusBar, value)
|
||||
local width = statusBar:GetWidth();
|
||||
if width and width > 0.0 then
|
||||
local min, max = statusBar:GetMinMaxValues();
|
||||
local percent = ClampedPercentageBetween(value, min, max);
|
||||
if percent == 0.0 or percent == 1.0 then
|
||||
statusBar:SetValue(value);
|
||||
else
|
||||
local numPixels = PixelUtil.GetNearestPixelSize(statusBar:GetWidth() * percent, statusBar:GetEffectiveScale());
|
||||
local roundedValue = Lerp(min, max, numPixels / width);
|
||||
statusBar:SetValue(roundedValue);
|
||||
end
|
||||
else
|
||||
statusBar:SetValue(value);
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,3 @@
|
||||
-- Backport of FrameXML/TimeUtil.lua (feasible subset) to vanilla 1.12 /
|
||||
-- Lua 5.0.
|
||||
--
|
||||
-- Ports the deprecated-but-widely-used duration formatters (SecondsToTime,
|
||||
-- SecondsToClock, SecondsToTimeAbbrev) plus the unit constants/helpers. The
|
||||
-- SecondsFormatterMixin (the modern replacement) is omitted — it's a heavy
|
||||
-- mixin class little addon code targets on classic.
|
||||
--
|
||||
-- 5.0 notes: `%` -> math.mod, `s:format()` -> string.format. The FrameXML
|
||||
-- format-string globals these functions consume (DAYS_ABBR, D_HOURS,
|
||||
-- HOURS_MINUTES_SECONDS, the *_ONELETTER_ABBR set, …) are defined in the
|
||||
-- locale layer (locales/enUS.lua), which loads first.
|
||||
|
||||
SECONDS_PER_MIN = 60;
|
||||
SECONDS_PER_HOUR = 60 * SECONDS_PER_MIN;
|
||||
SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR;
|
||||
@@ -52,67 +39,3 @@ function SecondsToClock(seconds, displayZeroHours)
|
||||
return string.format(MINUTES_SECONDS, units.minutes, units.seconds);
|
||||
end
|
||||
end
|
||||
|
||||
-- 1.12's native unit strings are bare labels ("Hr", "Min") with no `%d`,
|
||||
-- unlike modern's "%d Hr"; insert the count correctly for either shape.
|
||||
local function FormatUnit(fmt, count)
|
||||
if string.find(fmt, "%%d") then
|
||||
return string.format(fmt, count);
|
||||
end
|
||||
return count .. " " .. fmt;
|
||||
end
|
||||
|
||||
function SecondsToTime(seconds, noSeconds, notAbbreviated, maxCount, roundUp)
|
||||
local time = "";
|
||||
local count = 0;
|
||||
local tempTime;
|
||||
seconds = roundUp and math.ceil(seconds) or math.floor(seconds);
|
||||
maxCount = maxCount or 2;
|
||||
|
||||
-- With a single term, use a higher 1.5x threshold; with 2+, 1.0x.
|
||||
local threshold = (maxCount > 1) and 1.0 or 1.5;
|
||||
|
||||
if seconds >= SECONDS_PER_DAY * threshold then
|
||||
count = count + 1;
|
||||
tempTime = (count == maxCount and roundUp) and math.ceil(seconds / SECONDS_PER_DAY)
|
||||
or math.floor(seconds / SECONDS_PER_DAY);
|
||||
time = FormatUnit(notAbbreviated and D_DAYS or DAYS_ABBR, tempTime);
|
||||
seconds = math.mod(seconds, SECONDS_PER_DAY);
|
||||
end
|
||||
if count < maxCount and seconds >= SECONDS_PER_HOUR * threshold then
|
||||
count = count + 1;
|
||||
if time ~= "" then time = time .. TIME_UNIT_DELIMITER; end
|
||||
tempTime = (count == maxCount and roundUp) and math.ceil(seconds / SECONDS_PER_HOUR)
|
||||
or math.floor(seconds / SECONDS_PER_HOUR);
|
||||
time = time .. FormatUnit(notAbbreviated and D_HOURS or HOURS_ABBR, tempTime);
|
||||
seconds = math.mod(seconds, SECONDS_PER_HOUR);
|
||||
end
|
||||
if count < maxCount and seconds >= SECONDS_PER_MIN * threshold then
|
||||
count = count + 1;
|
||||
if time ~= "" then time = time .. TIME_UNIT_DELIMITER; end
|
||||
tempTime = (count == maxCount and roundUp) and math.ceil(seconds / SECONDS_PER_MIN)
|
||||
or math.floor(seconds / SECONDS_PER_MIN);
|
||||
time = time .. FormatUnit(notAbbreviated and D_MINUTES or MINUTES_ABBR, tempTime);
|
||||
seconds = math.mod(seconds, SECONDS_PER_MIN);
|
||||
end
|
||||
if count < maxCount and seconds > 0 and not noSeconds then
|
||||
if time ~= "" then time = time .. TIME_UNIT_DELIMITER; end
|
||||
time = time .. FormatUnit(notAbbreviated and D_SECONDS or SECONDS_ABBR, seconds);
|
||||
end
|
||||
return time;
|
||||
end
|
||||
|
||||
-- Returns (formatString, value) for the single largest applicable unit.
|
||||
function SecondsToTimeAbbrev(seconds, thresholdOverride)
|
||||
local threshold = thresholdOverride or 1.5;
|
||||
if seconds >= SECONDS_PER_DAY * threshold then
|
||||
return DAY_ONELETTER_ABBR, math.ceil(seconds / SECONDS_PER_DAY);
|
||||
end
|
||||
if seconds >= SECONDS_PER_HOUR * threshold then
|
||||
return HOUR_ONELETTER_ABBR, math.ceil(seconds / SECONDS_PER_HOUR);
|
||||
end
|
||||
if seconds >= SECONDS_PER_MIN * threshold then
|
||||
return MINUTE_ONELETTER_ABBR, math.ceil(seconds / SECONDS_PER_MIN);
|
||||
end
|
||||
return SECOND_ONELETTER_ABBR, seconds;
|
||||
end
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
BINDING_NAME_AOELOOT = "AoE Loot"
|
||||
BINDING_NAME_AOELOOT = "AoE Loot";
|
||||
BINDING_NAME_FOCUSTARGET = "Focus Target";
|
||||
BINDING_NAME_TARGETFOCUS = "Target Focus";
|
||||
COPPER_AMOUNT_SYMBOL = "c";
|
||||
FOCUSTARGET = "Focus Target";
|
||||
ITEMS_VARIABLE_QUANTITY = "%d |4Item:Items;";
|
||||
GENERIC_FRACTION_STRING = "%d/%d";
|
||||
GOLD_AMOUNT_SYMBOL = "g";
|
||||
HOURS_MINUTES_SECONDS = "%.2d:%.2d:%.2d";
|
||||
ITEMS_EQUIPPED = "%d |4item:items; equipped";
|
||||
ITEMS_IN_INVENTORY = "%d |4item:items; in inventory";
|
||||
ITEM_SLOTS_IGNORED = "%d |4slot:slots; ignored";
|
||||
ITEM_MISSING = "%s missing";
|
||||
ITEMS_NOT_IN_INVENTORY = "%d |4item:items; missing";
|
||||
ITEMS_VARIABLE_QUANTITY = "%d |4Item:Items;";
|
||||
ITEM_MISSING = "%s missing";
|
||||
ITEM_SLOTS_IGNORED = "%d |4slot:slots; ignored";
|
||||
LARGE_NUMBER_SEPERATOR = ",";
|
||||
MINUTES_SECONDS = "%.2d:%.2d";
|
||||
PERCENTAGE_STRING = "%d%%";
|
||||
SILVER_AMOUNT_SYMBOL = "s";
|
||||
SLASH_CLEARFOCUS1 = "/clearfocus";
|
||||
SLASH_CLEARFOCUS2 = "/clearfocus";
|
||||
SLASH_EQUIP_SET1 = "/equipset";
|
||||
@@ -15,25 +23,3 @@ SLASH_EQUIP_SET2 = "/equipset";
|
||||
SLASH_FOCUS1 = "/focus";
|
||||
SLASH_FOCUS2 = "/focus";
|
||||
TARGETFOCUS = "Target Focus";
|
||||
|
||||
LARGE_NUMBER_SEPERATOR = ",";
|
||||
PERCENTAGE_STRING = "%d%%";
|
||||
GENERIC_FRACTION_STRING = "%d/%d";
|
||||
GOLD_AMOUNT_SYMBOL = "g";
|
||||
SILVER_AMOUNT_SYMBOL = "s";
|
||||
COPPER_AMOUNT_SYMBOL = "c";
|
||||
TIME_UNIT_DELIMITER = " ";
|
||||
HOURS_MINUTES_SECONDS = "%.2d:%.2d:%.2d";
|
||||
MINUTES_SECONDS = "%.2d:%.2d";
|
||||
DAYS_ABBR = "%d |4Day:Days;";
|
||||
HOURS_ABBR = "%d |4Hr:Hr;";
|
||||
MINUTES_ABBR = "%d |4Min:Min;";
|
||||
SECONDS_ABBR = "%d |4Sec:Sec;";
|
||||
D_DAYS = "%d |4Day:Days;";
|
||||
D_HOURS = "%d |4Hour:Hours;";
|
||||
D_MINUTES = "%d |4Minute:Minutes;";
|
||||
D_SECONDS = "%d |4Second:Seconds;";
|
||||
DAY_ONELETTER_ABBR = "%d d";
|
||||
HOUR_ONELETTER_ABBR = "%d h";
|
||||
MINUTE_ONELETTER_ABBR = "%d m";
|
||||
SECOND_ONELETTER_ABBR = "%d s";
|
||||
|
||||
@@ -47,6 +47,7 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
|
||||
| [NamePlate](docs/API.md#nameplate) | `C_NamePlate.GetNamePlateForGUID`, `C_NamePlate.GetNamePlateForUnit`, `C_NamePlate.GetNamePlateGUIDs`, `C_NamePlate.GetNamePlates` |
|
||||
| [NameCache](docs/API.md#namecache) | `C_CreatureInfo.GetCreatureID`, `C_PlayerCache.GetPlayerInfoByName`, `C_PlayerCache.IsEnabled`, `C_PlayerCache.IsScanEnabled`, `C_PlayerCache.RememberPlayer`, `C_PlayerCache.SetEnabled`, `C_PlayerCache.SetScanEnabled`, `C_PlayerInfo.GUIDIsCreature`, `C_PlayerInfo.GUIDIsGameObject`, `C_PlayerInfo.GUIDIsPet`, `C_PlayerInfo.GUIDIsPlayer`, `GetPlayerInfoByGUID`, `UnitNameFromGUID` |
|
||||
| [Quest](docs/API.md#quest) | `C_QuestLog.GetQuestDetails`, `C_QuestLog.GetQuestIDForLogIndex`, `C_QuestLog.GetTitleForQuestID`, `C_QuestLog.IsOnQuest`, `C_QuestLog.IsQuestDataCachedByID`, `C_QuestLog.IsUnitOnQuest`, `C_QuestLog.RequestLoadQuestByID`, `GetQuestLogLeaderBoardID` |
|
||||
| [Screen](docs/API.md#screen) | `GetPhysicalScreenSize` |
|
||||
| [Spell](docs/API.md#spell) | `C_Spell.CancelSpellByID`, `C_Spell.CastAtCursor`, `C_Spell.CastAtUnit`, `C_Spell.DoesSpellExist`, `C_Spell.GetSchoolString`, `C_Spell.GetSpellCooldown`, `C_Spell.GetSpellDescription`, `C_Spell.GetSpellInfo`, `C_Spell.GetSpellLink`, `C_Spell.GetSpellMechanicByID`, `C_Spell.GetSpellName`, `C_Spell.GetSpellPowerCost`, `C_Spell.GetSpellRadius`, `C_Spell.GetSpellReagents`, `C_Spell.GetSpellSubtext`, `C_Spell.GetSpellTexture`, `C_Spell.IsAutoAttackSpell`, `C_Spell.IsCurrentSpell`, `C_Spell.IsRangedAutoAttackSpell`, `C_Spell.IsSelfBuff`, `C_Spell.IsSpellHarmful`, `C_Spell.IsSpellHelpful`, `C_Spell.IsSpellPassive`, `C_Spell.IsSpellUsable`, `C_Spell.SpellHasRange`, `CancelSpellByName`, `CastingInfo`, `CastSpellNoToggle`, `ChannelInfo`, `GetCraftSpellID`, `GetSpellInfo`, `GetSpellLink`, `GetSpellRadius`, `GetSpellSchool`, `IsHarmfulSpell`, `IsHelpfulSpell`, `IsPassiveSpell`, `IsPlayerSpell`, `IsSpellKnown`, `IsUsableSpell`, `SpellHasRange`, `UnitCastingInfo`, `UnitChannelInfo` |
|
||||
| [SpellBook](docs/API.md#spellbook) | `C_SpellBook.GetCurrentLevelSpells`, `C_SpellBook.GetSpellLevelLearned`, `C_SpellBook.GetSpellSkillLine`, `C_SpellBook.IsAutoAttackSpellBookItem`, `C_SpellBook.IsRangedAutoAttackSpellBookItem`, `FindSpellBookSlotByID` |
|
||||
| [State](docs/API.md#state) | `CancelShapeshiftForm`, `Dismount`, `GetMirrorTimerInfo`, `GetMirrorTimerProgress`, `GetShapeshiftFormID`, `IsAssistingRitual`, `IsFalling`, `IsInGroup`, `IsInRaid`, `IsLoggedIn`, `IsMounted`, `IsStealthed`, `IsSwimming` |
|
||||
|
||||
+37
@@ -270,6 +270,9 @@ build instructions.
|
||||
- [`C_QuestLog.IsQuestDataCachedByID(questID)`](#c_questlogisquestdatacachedbyidquestid)
|
||||
- [`GetQuestLogLeaderBoardID(objectiveIndex [, questIndex])`](#getquestlogleaderboardidobjectiveindex--questindex)
|
||||
|
||||
- [Screen](#screen)
|
||||
- [`GetPhysicalScreenSize()`](#getphysicalscreensize)
|
||||
|
||||
- [Spell](#spell)
|
||||
- [`C_Spell.DoesSpellExist(spellID)`](#c_spelldoesspellexistspellid)
|
||||
- [`C_Spell.GetSchoolString(schoolMask)`](#c_spellgetschoolstringschoolmask)
|
||||
@@ -6609,6 +6612,40 @@ A separate function keeps the existing call wire-compatible.
|
||||
> first, skip zero slots, then the item array. 1-based `objectiveIndex`
|
||||
> counts only non-empty slots.
|
||||
|
||||
## Screen
|
||||
|
||||
### `GetPhysicalScreenSize()`
|
||||
|
||||
Returns `(widthPixels, heightPixels)` — the display's physical
|
||||
resolution in pixels. Modern WoW added this as a native in 7.0
|
||||
(Legion); FrameXML's `PixelUtil` builds its entire pixel↔UI-unit
|
||||
conversion on it (`768.0 / physicalHeight`), which is what lets addons
|
||||
snap frames to whole pixels for crisp borders at fractional UI scales.
|
||||
|
||||
```lua
|
||||
local w, h = GetPhysicalScreenSize() -- e.g. 1920, 1080
|
||||
```
|
||||
|
||||
Vanilla 1.12 has no such native — and no pre-parsed pixel-dimension
|
||||
global. `GetScreenWidth()`/`GetScreenHeight()` return UI-space units
|
||||
(derived from the UIParent aspect ratio at `[UIParent + 0x7c]`), not
|
||||
pixels. The engine only ever holds the current mode as the
|
||||
`gxResolution` CVar string (`"WIDTHxHEIGHT"`); both `SetScreenResolution`
|
||||
(`0x0048bfd0`) and the display-init path sscanf it on demand rather
|
||||
than caching dimensions anywhere. So we read the `gxResolution` CVar
|
||||
directly through the engine's by-name lookup (`FUN_FIND_CVAR`, value at
|
||||
`+0x20`) and parse it — the same source of truth the engine uses.
|
||||
|
||||
Returns `1024, 768` (→ factor 1.0, making PixelUtil a 1:1 no-op) when
|
||||
the CVar is missing or unparseable. Caveat: in windowed mode
|
||||
`gxResolution` reflects the configured render resolution, which may
|
||||
differ from the OS window's client size — vanilla exposes no separate
|
||||
window-pixel query.
|
||||
|
||||
The companion `PixelUtil` table (`PixelUtil.SetPoint`/`SetSize`/
|
||||
`SetStatusBarValue`/…) is provided Lua-side by the ClassicAPI addon and
|
||||
consumes this function.
|
||||
|
||||
## Spell
|
||||
|
||||
> **All `C_Spell.*` functions in this section accept any spell
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `GetPhysicalScreenSize()` → (widthPixels, heightPixels) — the
|
||||
// display's physical resolution in pixels. Modern WoW added this as a
|
||||
// native in 7.0 (Legion); FrameXML's `PixelUtil` hangs its entire
|
||||
// pixel↔UI-unit conversion off it (`768.0 / physicalHeight`), which is
|
||||
// what lets addons snap frames to whole pixels.
|
||||
//
|
||||
// Vanilla 1.12 has no such native and no pre-parsed pixel-dimension
|
||||
// global — the engine only ever holds the current mode as the
|
||||
// `gxResolution` CVar string ("WIDTHxHEIGHT"). `Script_GetScreenWidth`
|
||||
// / `GetScreenHeight` return UI-space units (derived from the UIParent
|
||||
// aspect ratio at `[UIParent + 0x7c]`), not pixels, so they can't
|
||||
// serve. `SetScreenResolution` (`FUN_0048bfd0`) and the display-init
|
||||
// path both sscanf the `gxResolution` value on demand rather than
|
||||
// caching width/height anywhere — so the CVar string is the
|
||||
// authoritative current-resolution source, and we parse it the same
|
||||
// way the engine does.
|
||||
//
|
||||
// Returns 1024x768 (→ factor 1.0, i.e. 1 pixel == 1 UI unit, making
|
||||
// PixelUtil a no-op pass-through) when the CVar is missing or
|
||||
// unparseable. Caveat: in windowed mode `gxResolution` reflects the
|
||||
// configured render resolution, which may differ from the OS window's
|
||||
// client size — vanilla exposes no separate window-pixel query.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Screen::Info {
|
||||
|
||||
namespace {
|
||||
|
||||
using FindCVar_t = const uint8_t *(__fastcall *)(const char *name);
|
||||
|
||||
// Parses "WIDTHxHEIGHT" (e.g. "1920x1080"). Tolerant of the separator
|
||||
// (any run of non-digits between the two numbers) so an uppercase 'X'
|
||||
// or stray spaces don't break it. Returns false unless both numbers
|
||||
// are present and positive.
|
||||
bool ParseResolution(const char *s, int *outW, int *outH) {
|
||||
if (s == nullptr)
|
||||
return false;
|
||||
|
||||
const char *p = s;
|
||||
int w = 0;
|
||||
bool gotW = false;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
w = w * 10 + (*p - '0');
|
||||
++p;
|
||||
gotW = true;
|
||||
}
|
||||
if (!gotW)
|
||||
return false;
|
||||
|
||||
while (*p != '\0' && (*p < '0' || *p > '9')) // skip the 'x' separator
|
||||
++p;
|
||||
|
||||
int h = 0;
|
||||
bool gotH = false;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
h = h * 10 + (*p - '0');
|
||||
++p;
|
||||
gotH = true;
|
||||
}
|
||||
if (!gotH || w <= 0 || h <= 0)
|
||||
return false;
|
||||
|
||||
*outW = w;
|
||||
*outH = h;
|
||||
return true;
|
||||
}
|
||||
|
||||
int __fastcall Script_GetPhysicalScreenSize(void *L) {
|
||||
int width = 1024;
|
||||
int height = 768; // fallback → 768/768 = factor 1.0 (no-op snapping)
|
||||
|
||||
auto findCVar = reinterpret_cast<FindCVar_t>(Offsets::FUN_FIND_CVAR);
|
||||
const uint8_t *cvar = findCVar("gxResolution");
|
||||
if (cvar != nullptr) {
|
||||
const char *value = *reinterpret_cast<const char *const *>(
|
||||
cvar + Offsets::OFF_CVAR_VALUE_STR);
|
||||
ParseResolution(value, &width, &height);
|
||||
}
|
||||
|
||||
Game::Lua::PushNumber(L, width);
|
||||
Game::Lua::PushNumber(L, height);
|
||||
return 2;
|
||||
}
|
||||
|
||||
void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterGlobalFunction("GetPhysicalScreenSize",
|
||||
&Script_GetPhysicalScreenSize);
|
||||
}
|
||||
|
||||
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace Screen::Info
|
||||
Reference in New Issue
Block a user