Extract shared lua.zig and add DLL-side marker permission check

Move Lua C API wrappers from main.zig's lua struct into src/lua.zig
so both main.zig and markers.zig import from the same source. Remove
duplicate lapi struct from markers.zig.

Add DLL-side permission gating on WorldMarker/ClearWorldMarker: calls
WoW's IsPartyLeader (0x4e9130) and IsRaidOfficer (0x4bb910) C
functions directly. Requires party leader, raid leader, or raid
assist to place or clear markers. SetMarkerDef/ClearMarkerDef remain
ungated (addon validates sender before calling).
This commit is contained in:
MarcelineVQ
2026-03-02 12:59:38 -08:00
parent db1243d136
commit 3c15c8650a
3 changed files with 235 additions and 237 deletions
+155
View File
@@ -0,0 +1,155 @@
//! WoW 1.12.1 Lua C API wrappers (all __fastcall, L in ECX)
const std = @import("std");
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
pub const State = *anyopaque;
pub fn getContext() State {
const f: *const fn () callconv(fc) State = @ptrFromInt(0x7040D0);
return f();
}
pub fn gettop(L: State) i32 {
const f: *const fn (State) callconv(fc) i32 = @ptrFromInt(0x6F3070);
return f(L);
}
pub fn settop(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3080);
f(L, index);
}
pub fn pop(L: State, n: i32) void {
settop(L, -n - 1);
}
pub fn pushvalue(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3350);
f(L, index);
}
pub fn remove(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F30D0);
f(L, index);
}
pub fn insert(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F31A0);
f(L, index);
}
pub fn typeOf(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3400);
return f(L, index);
}
pub fn typeName(L: State, tp: i32) [*:0]const u8 {
const f: *const fn (State, i32) callconv(fc) [*:0]const u8 = @ptrFromInt(0x6F3480);
return f(L, tp);
}
pub fn isnumber(L: State, index: i32) bool {
const f: *const fn (State, i32) callconv(fc) u32 = @ptrFromInt(0x6F34D0);
return f(L, index) != 0;
}
pub fn isstring(L: State, index: i32) bool {
const f: *const fn (State, i32) callconv(fc) u32 = @ptrFromInt(0x6F3510);
return f(L, index) != 0;
}
pub fn pushnil(L: State) void {
const f: *const fn (State) callconv(fc) void = @ptrFromInt(0x6F37F0);
f(L);
}
pub fn pushnumber(L: State, n: f64) void {
// __thiscall: ECX=L, f64 on stack [EBP+8]/[EBP+0xc], ret 8.
const f: *const fn (State, f64) callconv(.{ .x86_thiscall = .{} }) void = @ptrFromInt(0x6F3810);
f(L, n);
}
pub fn pushstring(L: State, s: [*:0]const u8) void {
const f: *const fn (State, [*:0]const u8) callconv(fc) void = @ptrFromInt(0x6F3890);
f(L, s);
}
pub fn pushboolean(L: State, b: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F39F0);
f(L, b);
}
pub fn pushcclosure(L: State, func: usize, n: i32) void {
const f: *const fn (State, usize, i32) callconv(fc) void = @ptrFromInt(0x6F3920);
f(L, func, n);
}
pub fn tonumber(L: State, index: i32) f64 {
const f: *const fn (State, i32) callconv(fc) f64 = @ptrFromInt(0x6F3620);
return f(L, index);
}
pub fn tostring(L: State, index: i32) ?[*:0]const u8 {
const f: *const fn (State, i32) callconv(fc) ?[*:0]const u8 = @ptrFromInt(0x6F3690);
return f(L, index);
}
pub fn toboolean(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3660);
return f(L, index);
}
pub fn newtable(L: State) void {
const f: *const fn (State) callconv(fc) void = @ptrFromInt(0x6F3C90);
f(L);
}
pub fn settable(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3E20);
f(L, index);
}
pub fn gettable(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3A40);
f(L, index);
}
pub fn next(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F4450);
return f(L, index);
}
pub fn pcall(L: State, nargs: i32, nresults: i32, errfunc: i32) i32 {
const f: *const fn (State, i32, i32, i32) callconv(fc) i32 = @ptrFromInt(0x6F41A0);
return f(L, nargs, nresults, errfunc);
}
pub fn luaError(L: State, msg: [*:0]const u8) void {
asm volatile (
\\push %[msg]
\\push %[L]
\\call *%[func]
\\add $8, %%esp
:
: [L] "r" (@intFromPtr(L)),
[msg] "r" (@intFromPtr(msg)),
[func] "r" (@as(u32, 0x6F4940)),
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true }
);
}
pub const LuaReg = extern struct {
name: ?[*:0]const u8,
func: usize,
};
pub fn openlib(L: State, libname: ?[*:0]const u8, funcs: [*]const LuaReg, nup: i32) void {
const f: *const fn (State, ?[*:0]const u8, [*]const LuaReg, i32) callconv(fc) void = @ptrFromInt(0x6F4DC0);
f(L, libname, funcs, nup);
}
pub fn checknumber(L: State, index: i32) f64 {
const f: *const fn (State, i32) callconv(fc) f64 = @ptrFromInt(0x6F4C80);
return f(L, index);
}
+1 -152
View File
@@ -44,158 +44,7 @@ fn luaProtectionDetour() callconv(sc) void {}
// Lua C API wrappers (WoW 1.12.1 — all __fastcall, L in ECX)
// =============================================================================
pub const lua = struct {
pub const State = *anyopaque;
pub fn getContext() State {
const f: *const fn () callconv(fc) State = @ptrFromInt(0x7040D0);
return f();
}
pub fn gettop(L: State) i32 {
const f: *const fn (State) callconv(fc) i32 = @ptrFromInt(0x6F3070);
return f(L);
}
pub fn settop(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3080);
f(L, index);
}
pub fn pop(L: State, n: i32) void {
settop(L, -n - 1);
}
pub fn pushvalue(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3350);
f(L, index);
}
pub fn remove(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F30D0);
f(L, index);
}
pub fn insert(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F31A0);
f(L, index);
}
pub fn typeOf(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3400);
return f(L, index);
}
pub fn typeName(L: State, tp: i32) [*:0]const u8 {
const f: *const fn (State, i32) callconv(fc) [*:0]const u8 = @ptrFromInt(0x6F3480);
return f(L, tp);
}
pub fn isnumber(L: State, index: i32) bool {
const f: *const fn (State, i32) callconv(fc) u32 = @ptrFromInt(0x6F34D0);
return f(L, index) != 0;
}
pub fn isstring(L: State, index: i32) bool {
const f: *const fn (State, i32) callconv(fc) u32 = @ptrFromInt(0x6F3510);
return f(L, index) != 0;
}
pub fn pushnil(L: State) void {
const f: *const fn (State) callconv(fc) void = @ptrFromInt(0x6F37F0);
f(L);
}
pub fn pushnumber(L: State, n: f64) void {
// __thiscall: ECX=L, f64 on stack [EBP+8]/[EBP+0xc], ret 8.
const f: *const fn (State, f64) callconv(.{ .x86_thiscall = .{} }) void = @ptrFromInt(0x6F3810);
f(L, n);
}
pub fn pushstring(L: State, s: [*:0]const u8) void {
const f: *const fn (State, [*:0]const u8) callconv(fc) void = @ptrFromInt(0x6F3890);
f(L, s);
}
pub fn pushboolean(L: State, b: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F39F0);
f(L, b);
}
pub fn pushcclosure(L: State, func: usize, n: i32) void {
const f: *const fn (State, usize, i32) callconv(fc) void = @ptrFromInt(0x6F3920);
f(L, func, n);
}
pub fn tonumber(L: State, index: i32) f64 {
const f: *const fn (State, i32) callconv(fc) f64 = @ptrFromInt(0x6F3620);
return f(L, index);
}
pub fn tostring(L: State, index: i32) ?[*:0]const u8 {
const f: *const fn (State, i32) callconv(fc) ?[*:0]const u8 = @ptrFromInt(0x6F3690);
return f(L, index);
}
pub fn toboolean(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3660);
return f(L, index);
}
pub fn newtable(L: State) void {
const f: *const fn (State) callconv(fc) void = @ptrFromInt(0x6F3C90);
f(L);
}
pub fn settable(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3E20);
f(L, index);
}
pub fn gettable(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3A40);
f(L, index);
}
pub fn next(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F4450);
return f(L, index);
}
pub fn pcall(L: State, nargs: i32, nresults: i32, errfunc: i32) i32 {
const f: *const fn (State, i32, i32, i32) callconv(fc) i32 = @ptrFromInt(0x6F41A0);
return f(L, nargs, nresults, errfunc);
}
pub fn luaError(L: State, msg: [*:0]const u8) void {
asm volatile (
\\push %[msg]
\\push %[L]
\\call *%[func]
\\add $8, %%esp
:
: [L] "r" (@intFromPtr(L)),
[msg] "r" (@intFromPtr(msg)),
[func] "r" (@as(u32, 0x6F4940)),
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true }
);
}
pub const LuaReg = extern struct {
name: ?[*:0]const u8,
func: usize,
};
pub fn openlib(L: State, libname: ?[*:0]const u8, funcs: [*]const LuaReg, nup: i32) void {
const f: *const fn (State, ?[*:0]const u8, [*]const LuaReg, i32) callconv(fc) void = @ptrFromInt(0x6F4DC0);
f(L, libname, funcs, nup);
}
pub fn checknumber(L: State, index: i32) f64 {
const f: *const fn (State, i32) callconv(fc) f64 = @ptrFromInt(0x6F4C80);
return f(L, index);
}
};
pub const lua = @import("lua.zig");
// =============================================================================
// Game function wrappers
+79 -85
View File
@@ -17,6 +17,7 @@
const std = @import("std");
const hook = @import("zhook");
const lua = @import("../lua.zig");
const o = @import("offsets.zig");
const wow = @import("../outline/wow.zig");
const con = @import("../console.zig");
@@ -107,52 +108,35 @@ const DespawningEntity = struct {
};
var despawning: [MAX_DESPAWNING]?DespawningEntity = .{null} ** MAX_DESPAWNING;
// =============================================================================
// Lua C API (WoW 1.12.1 — all __fastcall, L in ECX)
// =============================================================================
const fc = std.builtin.CallingConvention{ .x86_fastcall = .{} };
const sc = std.builtin.CallingConvention{ .x86_stdcall = .{} };
const lapi = struct {
fn gettop(L: u32) i32 {
const f: *const fn (u32) callconv(fc) i32 = @ptrFromInt(0x6F3070);
return f(L);
}
// =============================================================================
// Permission check — leader or raid officer required
// =============================================================================
fn isnumber(L: u32, index: i32) bool {
const f: *const fn (u32, i32) callconv(fc) u32 = @ptrFromInt(0x6F34D0);
return f(L, index) != 0;
}
/// WoW Lua C functions: ECX=L, return count of pushed values.
const LuaCFn = *const fn (lua.State) callconv(.c) u32;
const FN_IS_PARTY_LEADER: LuaCFn = @ptrFromInt(0x004e9130);
const FN_IS_RAID_OFFICER: LuaCFn = @ptrFromInt(0x004bb910);
fn isstring(L: u32, index: i32) bool {
const f: *const fn (u32, i32) callconv(fc) u32 = @ptrFromInt(0x6F3510);
return f(L, index) != 0;
}
/// Call a WoW Lua C function that pushes 1 value, check if it pushed
/// a truthy value (non-nil), then pop the result.
fn luaBoolCheck(L: lua.State, func: LuaCFn) bool {
const before = lua.gettop(L);
_ = func(L);
const after = lua.gettop(L);
if (after <= before) return false;
const is_nil = lua.typeOf(L, -1) == 0;
lua.settop(L, before);
return !is_nil;
}
fn tonumber(L: u32, index: i32) f64 {
const f: *const fn (u32, i32) callconv(fc) f64 = @ptrFromInt(0x6F3620);
return f(L, index);
}
fn tostring(L: u32, index: i32) ?[*:0]const u8 {
const f: *const fn (u32, i32) callconv(fc) ?[*:0]const u8 = @ptrFromInt(0x6F3690);
return f(L, index);
}
fn pushstring(L: u32, s: [*:0]const u8) void {
const f: *const fn (u32, [*:0]const u8) callconv(fc) void = @ptrFromInt(0x6F3890);
f(L, s);
}
fn pushnumber(L: u32, n: f64) void {
// Ghidra confirms: __thiscall(ECX=L), double on stack as [EBP+8]/[EBP+0xc], ret 8.
// Can't use fastcall — our patched inreg would shove the f64's low dword into EDX.
const tc = std.builtin.CallingConvention{ .x86_thiscall = .{} };
const f: *const fn (u32, f64) callconv(tc) void = @ptrFromInt(0x6F3810);
f(L, n);
}
};
/// Check if the local player has permission to place/clear markers.
/// Requires: party leader, raid leader, or raid officer (assist).
fn canSetMarkers(L: lua.State) bool {
return luaBoolCheck(L, FN_IS_PARTY_LEADER) or luaBoolCheck(L, FN_IS_RAID_OFFICER);
}
// =============================================================================
// Position helpers
@@ -414,30 +398,35 @@ fn clearAllMarkers() void {
/// WorldMarker(1, x, y, z) — place at coordinates
/// WorldMarker(1, "target") — place at unit's current position
/// WorldMarker(1) — place at cursor terrain position
pub fn luaWorldMarker(L: u32) callconv(.c) u32 {
const nargs = lapi.gettop(L);
pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 {
if (!canSetMarkers(L)) {
con.print("[markers] WorldMarker: no permission (need leader/assist)\n");
return 0;
}
if (nargs < 1 or !lapi.isnumber(L, 1)) {
const nargs = lua.gettop(L);
if (nargs < 1 or !lua.isnumber(L, 1)) {
con.print("[markers] WorldMarker: expected index (1-5)\n");
return 0;
}
const raw_index = @as(i32, @intFromFloat(lapi.tonumber(L, 1)));
const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1)));
if (raw_index < 1 or raw_index > NUM_MARKERS) {
con.print("[markers] WorldMarker: index must be 1-5\n");
return 0;
}
const index: usize = @intCast(raw_index - 1);
if (nargs >= 4 and lapi.isnumber(L, 2)) {
if (nargs >= 4 and lua.isnumber(L, 2)) {
// WorldMarker(index, x, y, z)
const x: f32 = @floatCast(lapi.tonumber(L, 2));
const y: f32 = @floatCast(lapi.tonumber(L, 3));
const z: f32 = @floatCast(lapi.tonumber(L, 4));
const x: f32 = @floatCast(lua.tonumber(L, 2));
const y: f32 = @floatCast(lua.tonumber(L, 3));
const z: f32 = @floatCast(lua.tonumber(L, 4));
_ = placeMarker(index, .{ .x = x, .y = y, .z = z });
} else if (nargs >= 2 and lapi.isstring(L, 2)) {
} else if (nargs >= 2 and lua.isstring(L, 2)) {
// WorldMarker(index, "unitId")
const unit_id = lapi.tostring(L, 2) orelse {
const unit_id = lua.tostring(L, 2) orelse {
con.print("[markers] WorldMarker: invalid unit string\n");
return 0;
};
@@ -461,20 +450,25 @@ pub fn luaWorldMarker(L: u32) callconv(.c) u32 {
/// Lua: ClearWorldMarker([index])
/// ClearWorldMarker(1) — remove marker 1
/// ClearWorldMarker() — remove all markers
pub fn luaClearWorldMarker(L: u32) callconv(.c) u32 {
const nargs = lapi.gettop(L);
pub fn luaClearWorldMarker(L: lua.State) callconv(.c) u32 {
if (!canSetMarkers(L)) {
con.print("[markers] ClearWorldMarker: no permission (need leader/assist)\n");
return 0;
}
const nargs = lua.gettop(L);
if (nargs == 0) {
clearAllMarkers();
return 0;
}
if (!lapi.isnumber(L, 1)) {
if (!lua.isnumber(L, 1)) {
con.print("[markers] ClearWorldMarker: expected index (1-5) or no args\n");
return 0;
}
const raw_index = @as(i32, @intFromFloat(lapi.tonumber(L, 1)));
const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1)));
if (raw_index < 1 or raw_index > NUM_MARKERS) {
con.print("[markers] ClearWorldMarker: index must be 1-5\n");
return 0;
@@ -557,25 +551,25 @@ fn tickAnimations() void {
}
/// Lua: local x, y, z = GetPlayerPosition()
pub fn luaGetPlayerPosition(L: u32) callconv(.c) u32 {
pub fn luaGetPlayerPosition(L: lua.State) callconv(.c) u32 {
const player = wow.getLocalPlayer();
if (player == 0) return 0;
const pos = getUnitPosition(player);
lapi.pushnumber(L, @floatCast(pos.x));
lapi.pushnumber(L, @floatCast(pos.y));
lapi.pushnumber(L, @floatCast(pos.z));
lua.pushnumber(L, @floatCast(pos.x));
lua.pushnumber(L, @floatCast(pos.y));
lua.pushnumber(L, @floatCast(pos.z));
return 3;
}
/// Lua: local dist = DistanceToMark(index)
/// Returns distance in yards from player to the stored marker position,
/// or nil if the marker doesn't exist or there's no player.
pub fn luaDistanceToMark(L: u32) callconv(.c) u32 {
const nargs = lapi.gettop(L);
if (nargs < 1 or !lapi.isnumber(L, 1)) return 0;
pub fn luaDistanceToMark(L: lua.State) callconv(.c) u32 {
const nargs = lua.gettop(L);
if (nargs < 1 or !lua.isnumber(L, 1)) return 0;
const raw_index = @as(i32, @intFromFloat(lapi.tonumber(L, 1)));
const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1)));
if (raw_index < 1 or raw_index > NUM_MARKERS) return 0;
const index: usize = @intCast(raw_index - 1);
@@ -592,26 +586,26 @@ pub fn luaDistanceToMark(L: u32) callconv(.c) u32 {
const dz = player_pos.z - mark_pos.z;
const dist = @sqrt(dx * dx + dy * dy + dz * dz);
lapi.pushnumber(L, @floatCast(dist));
lua.pushnumber(L, @floatCast(dist));
return 1;
}
/// Lua: SetMarkerDef(index, x, y, z, areaId)
/// Store a marker definition without immediately spawning. Used by the addon
/// when receiving remote marker data — proximity respawn handles entity creation.
pub fn luaSetMarkerDef(L: u32) callconv(.c) u32 {
const nargs = lapi.gettop(L);
pub fn luaSetMarkerDef(L: lua.State) callconv(.c) u32 {
const nargs = lua.gettop(L);
if (nargs < 5) return 0;
if (!lapi.isnumber(L, 1) or !lapi.isnumber(L, 2) or !lapi.isnumber(L, 3) or !lapi.isnumber(L, 4) or !lapi.isnumber(L, 5)) return 0;
if (!lua.isnumber(L, 1) or !lua.isnumber(L, 2) or !lua.isnumber(L, 3) or !lua.isnumber(L, 4) or !lua.isnumber(L, 5)) return 0;
const raw_index = @as(i32, @intFromFloat(lapi.tonumber(L, 1)));
const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1)));
if (raw_index < 1 or raw_index > NUM_MARKERS) return 0;
const index: usize = @intCast(raw_index - 1);
const x: f32 = @floatCast(lapi.tonumber(L, 2));
const y: f32 = @floatCast(lapi.tonumber(L, 3));
const z: f32 = @floatCast(lapi.tonumber(L, 4));
const area_id: u32 = @intFromFloat(lapi.tonumber(L, 5));
const x: f32 = @floatCast(lua.tonumber(L, 2));
const y: f32 = @floatCast(lua.tonumber(L, 3));
const z: f32 = @floatCast(lua.tonumber(L, 4));
const area_id: u32 = @intFromFloat(lua.tonumber(L, 5));
// Clear existing entity if any (will be respawned by proximity check)
clearEntity(index);
@@ -628,15 +622,15 @@ pub fn luaSetMarkerDef(L: u32) callconv(.c) u32 {
/// Lua: ClearMarkerDef([index])
/// Clear a definition (and its entity if alive). No args = clear all.
pub fn luaClearMarkerDef(L: u32) callconv(.c) u32 {
const nargs = lapi.gettop(L);
pub fn luaClearMarkerDef(L: lua.State) callconv(.c) u32 {
const nargs = lua.gettop(L);
if (nargs == 0) {
clearAllMarkers();
return 0;
}
if (!lapi.isnumber(L, 1)) return 0;
const raw_index = @as(i32, @intFromFloat(lapi.tonumber(L, 1)));
if (!lua.isnumber(L, 1)) return 0;
const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1)));
if (raw_index < 1 or raw_index > NUM_MARKERS) return 0;
clearMarker(@intCast(raw_index - 1));
@@ -645,28 +639,28 @@ pub fn luaClearMarkerDef(L: u32) callconv(.c) u32 {
/// Lua: local x, y, z, areaId = GetMarkerDef(index)
/// Returns position and area ID for an active marker def, or nil if inactive.
pub fn luaGetMarkerDef(L: u32) callconv(.c) u32 {
const nargs = lapi.gettop(L);
if (nargs < 1 or !lapi.isnumber(L, 1)) return 0;
pub fn luaGetMarkerDef(L: lua.State) callconv(.c) u32 {
const nargs = lua.gettop(L);
if (nargs < 1 or !lua.isnumber(L, 1)) return 0;
const raw_index = @as(i32, @intFromFloat(lapi.tonumber(L, 1)));
const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1)));
if (raw_index < 1 or raw_index > NUM_MARKERS) return 0;
const index: usize = @intCast(raw_index - 1);
if (!marker_defs[index].active) return 0;
lapi.pushnumber(L, @floatCast(marker_defs[index].pos.x));
lapi.pushnumber(L, @floatCast(marker_defs[index].pos.y));
lapi.pushnumber(L, @floatCast(marker_defs[index].pos.z));
lapi.pushnumber(L, @floatCast(@as(f64, @floatFromInt(marker_defs[index].area_id))));
lua.pushnumber(L, @floatCast(marker_defs[index].pos.x));
lua.pushnumber(L, @floatCast(marker_defs[index].pos.y));
lua.pushnumber(L, @floatCast(marker_defs[index].pos.z));
lua.pushnumber(L, @floatCast(@as(f64, @floatFromInt(marker_defs[index].area_id))));
return 4;
}
/// Lua: local areaId = GetCurrentAreaId()
/// Returns the current zone area ID from the game global.
pub fn luaGetCurrentAreaId(L: u32) callconv(.c) u32 {
pub fn luaGetCurrentAreaId(L: lua.State) callconv(.c) u32 {
const area_id = hook.readMem(u32, o.ZONE_AREA_ID);
lapi.pushnumber(L, @floatCast(@as(f64, @floatFromInt(area_id))));
lua.pushnumber(L, @floatCast(@as(f64, @floatFromInt(area_id))));
return 1;
}