mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 07:03:16 +00:00
fix telekenesis crash
This commit is contained in:
parent
1717a46b7a
commit
82256e68e8
9 changed files with 958 additions and 908 deletions
|
@ -108,13 +108,35 @@ impl EntityID {
|
|||
|
||||
pub fn kill(self) {
|
||||
// Shouldn't ever error.
|
||||
for (i, id) in raw::physics_body_id_get_from_entity(self, None)
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let n = 17000.0 + (64.0 * (self.0.get() as usize + i) as f64);
|
||||
let _ = raw::physics_body_id_set_transform(*id, n, n, 0.0, 0.0, 0.0, 0.0);
|
||||
let body_id = raw::physics_body_id_get_from_entity(self, None).unwrap_or_default();
|
||||
if !body_id.is_empty() {
|
||||
for com in raw::entity_get_with_tag("ew_peer".into())
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
e.map(|e| {
|
||||
e.try_get_first_component_including_disabled::<TelekinesisComponent>(None)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.flatten()
|
||||
{
|
||||
if body_id.contains(&com.get_body_id()) {
|
||||
let _ = raw::component_set_value(*com, "mState", 0);
|
||||
}
|
||||
}
|
||||
for (i, id) in body_id.iter().enumerate() {
|
||||
let n = 17000.0;
|
||||
let _ = raw::physics_body_id_set_transform(
|
||||
*id,
|
||||
n + 64.0 * self.0.get() as f64,
|
||||
n + 64.0 * i as f64,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
let _ = raw::entity_kill(self);
|
||||
}
|
||||
|
@ -526,6 +548,12 @@ impl StatusEffectDataComponent {
|
|||
}
|
||||
}
|
||||
|
||||
impl TelekinesisComponent {
|
||||
pub fn get_body_id(self) -> PhysicsBodyID {
|
||||
raw::component_get_value_old::<PhysicsBodyID>(*self, "mBodyID").unwrap_or(PhysicsBodyID(0))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn game_print(value: impl AsRef<str>) {
|
||||
let _ = raw::game_print(value.as_ref().into());
|
||||
}
|
||||
|
@ -560,6 +588,21 @@ pub mod raw {
|
|||
ret.wrap_err_with(|| eyre!("Getting {field} for {component:?}"))
|
||||
}
|
||||
|
||||
pub(crate) fn component_get_value_old<T>(component: ComponentID, field: &str) -> eyre::Result<T>
|
||||
where
|
||||
T: LuaGetValue,
|
||||
{
|
||||
let lua = LuaState::current()?;
|
||||
lua.get_global(c"ComponentGetValue");
|
||||
lua.push_integer(component.0.into());
|
||||
lua.push_string(field);
|
||||
lua.call(2, T::size_on_stack())
|
||||
.wrap_err("Failed to call ComponentGetValue")?;
|
||||
let ret = T::get(lua, -1);
|
||||
lua.pop_last_n(T::size_on_stack());
|
||||
ret.wrap_err_with(|| eyre!("Getting {field} for {component:?}"))
|
||||
}
|
||||
|
||||
pub(crate) fn component_object_get_value<T>(
|
||||
component: ComponentID,
|
||||
object: &str,
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
|
||||
-- You're supposed to `dofile_once("path/to/load.lua")` this file.
|
||||
|
||||
|
||||
local orig_do_mod_appends = do_mod_appends
|
||||
|
||||
do_mod_appends = function(filename, ...)
|
||||
do_mod_appends = orig_do_mod_appends
|
||||
do_mod_appends(filename, ...)
|
||||
|
||||
local noitapatcher_path = string.match(filename, "(.*)/load.lua")
|
||||
if not noitapatcher_path then
|
||||
print("Couldn't detect NoitaPatcher path")
|
||||
end
|
||||
|
||||
__nsew_path = noitapatcher_path .. "/noitapatcher/nsew/"
|
||||
|
||||
package.cpath = package.cpath .. ";./" .. noitapatcher_path .. "/?.dll"
|
||||
package.path = package.path .. ";./" .. noitapatcher_path .. "/?.lua"
|
||||
|
||||
-- Lua's loader should now be setup properly:
|
||||
-- local np = require("noitapatcher")
|
||||
-- local nsew = require("noitapatcher.nsew")
|
||||
end
|
||||
-- You're supposed to `dofile_once("path/to/load.lua")` this file.
|
||||
|
||||
local orig_do_mod_appends = do_mod_appends
|
||||
|
||||
do_mod_appends = function(filename, ...)
|
||||
do_mod_appends = orig_do_mod_appends
|
||||
do_mod_appends(filename, ...)
|
||||
|
||||
local noitapatcher_path = string.match(filename, "(.*)/load.lua")
|
||||
if not noitapatcher_path then
|
||||
print("Couldn't detect NoitaPatcher path")
|
||||
end
|
||||
|
||||
__nsew_path = noitapatcher_path .. "/noitapatcher/nsew/"
|
||||
|
||||
package.cpath = package.cpath .. ";./" .. noitapatcher_path .. "/?.dll"
|
||||
package.path = package.path .. ";./" .. noitapatcher_path .. "/?.lua"
|
||||
|
||||
-- Lua's loader should now be setup properly:
|
||||
-- local np = require("noitapatcher")
|
||||
-- local nsew = require("noitapatcher.nsew")
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
---Native library. Primarily for internal use.
|
||||
---@module 'noitapatcher.nsew.native_dll'
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
native_dll = {}
|
||||
|
||||
---The NSEW support dll loaded in with `ffi.load`.
|
||||
native_dll.lib = ffi.load(__nsew_path .. "nsew_native.dll")
|
||||
|
||||
return native_dll
|
||||
---Native library. Primarily for internal use.
|
||||
---@module 'noitapatcher.nsew.native_dll'
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
native_dll = {}
|
||||
|
||||
---The NSEW support dll loaded in with `ffi.load`.
|
||||
native_dll.lib = ffi.load(__nsew_path .. "nsew_native.dll")
|
||||
|
||||
return native_dll
|
||||
|
|
|
@ -1,167 +1,167 @@
|
|||
---Rectangle utilities.
|
||||
---@module 'noitapatcher.nsew.rect'
|
||||
|
||||
---@class Rect
|
||||
local rect = {}
|
||||
|
||||
local ffi = require("ffi")
|
||||
local native_dll = require("noitapatcher.nsew.native_dll")
|
||||
|
||||
ffi.cdef([[
|
||||
|
||||
struct nsew_rectangle {
|
||||
int32_t left;
|
||||
int32_t top;
|
||||
int32_t right;
|
||||
int32_t bottom;
|
||||
};
|
||||
|
||||
|
||||
struct nsew_rectangle_optimiser;
|
||||
|
||||
struct nsew_rectangle_optimiser* rectangle_optimiser_new();
|
||||
void rectangle_optimiser_delete(struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
void rectangle_optimiser_reset(struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
void rectangle_optimiser_submit(struct nsew_rectangle_optimiser* rectangle_optimiser, struct nsew_rectangle* rectangle);
|
||||
void rectangle_optimiser_scan(struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
int32_t rectangle_optimiser_size(const struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
const struct nsew_rectangle* rectangle_optimiser_get(const struct nsew_rectangle_optimiser* rectangle_optimiser, int32_t index);
|
||||
|
||||
|
||||
struct lua_nsew_rectangle_optimiser {
|
||||
struct nsew_rectangle_optimiser* impl;
|
||||
};
|
||||
|
||||
]])
|
||||
|
||||
---@class Rectangle_fields
|
||||
---@field top integer
|
||||
---@field bottom integer
|
||||
---@field right integer
|
||||
---@field left integer
|
||||
|
||||
---@alias Rectangle Rectangle_mt | Rectangle_fields
|
||||
|
||||
---@class Optimiser_fields
|
||||
---@field top integer
|
||||
---@field bottom integer
|
||||
---@field right integer
|
||||
---@field left integer
|
||||
|
||||
---@alias Optimiser Optimiser_fields | Optimiser_mt
|
||||
|
||||
---@class Rectangle_mt
|
||||
local Rectangle_mt_index = {
|
||||
---@param r Rectangle
|
||||
---@return integer
|
||||
area = function(r)
|
||||
return (r.right - r.left) * (r.bottom - r.top)
|
||||
end,
|
||||
---@param r Rectangle
|
||||
---@return integer
|
||||
height = function(r)
|
||||
return r.bottom - r.top
|
||||
end,
|
||||
---@param r Rectangle
|
||||
---@return integer
|
||||
width = function(r)
|
||||
return r.right - r.left
|
||||
end,
|
||||
}
|
||||
local Rectangle_mt = {
|
||||
__index = Rectangle_mt_index,
|
||||
}
|
||||
|
||||
---@type fun(left, top, right, bottom): Rectangle
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
rect.Rectangle = ffi.metatype("struct nsew_rectangle", Rectangle_mt)
|
||||
|
||||
---Given an iterator that returns rectangles, return an iterator where the
|
||||
---rectangle extents never exceed `size`.
|
||||
---@param iterator fun(): Rectangle? returning rectangles
|
||||
---@param size integer maximum width and height
|
||||
---@return fun(): Rectangle? rectangles where the extents never exceed `size`
|
||||
function rect.parts(iterator, size)
|
||||
local region
|
||||
local posx
|
||||
local posy
|
||||
return function()
|
||||
if region == nil then
|
||||
region = iterator()
|
||||
if region == nil then
|
||||
return nil
|
||||
end
|
||||
posx = region.left
|
||||
posy = region.top
|
||||
end
|
||||
|
||||
local endx = math.min(posx + size, region.right)
|
||||
local endy = math.min(posy + size, region.bottom)
|
||||
|
||||
local ret = rect.Rectangle(posx, posy, endx, endy)
|
||||
|
||||
-- Setup for next iteration: place to the right, wraparound, or
|
||||
-- we're done with this region.
|
||||
if endx ~= region.right then
|
||||
posx = endx
|
||||
elseif endy ~= region.bottom then
|
||||
posx = region.left
|
||||
posy = endy
|
||||
else
|
||||
region = nil
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
---@class Optimiser_mt
|
||||
local Optimiser_mt_index = {
|
||||
submit = function(opt, rectangle)
|
||||
native_dll.lib.rectangle_optimiser_submit(opt.impl, rectangle)
|
||||
end,
|
||||
scan = function(opt)
|
||||
native_dll.lib.rectangle_optimiser_scan(opt.impl)
|
||||
end,
|
||||
reset = function(opt)
|
||||
native_dll.lib.rectangle_optimiser_reset(opt.impl)
|
||||
end,
|
||||
size = function(opt)
|
||||
return native_dll.lib.rectangle_optimiser_size(opt.impl)
|
||||
end,
|
||||
get = function(opt, index)
|
||||
return native_dll.lib.rectangle_optimiser_get(opt.impl, index)
|
||||
end,
|
||||
iterate = function(opt)
|
||||
local size = native_dll.lib.rectangle_optimiser_size(opt.impl)
|
||||
local index = 0
|
||||
return function()
|
||||
if index >= size then
|
||||
return nil
|
||||
end
|
||||
|
||||
local ret = native_dll.lib.rectangle_optimiser_get(opt.impl, index)
|
||||
index = index + 1
|
||||
return ret
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local Optimiser_mt = {
|
||||
__gc = function(opt)
|
||||
native_dll.lib.rectangle_optimiser_delete(opt.impl)
|
||||
end,
|
||||
__index = Optimiser_mt_index,
|
||||
}
|
||||
|
||||
---@type fun(unknown): Optimiser
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
rect.Optimiser = ffi.metatype("struct lua_nsew_rectangle_optimiser", Optimiser_mt)
|
||||
|
||||
---Create a new rectangle Optimiser
|
||||
---@return Optimiser optimiser
|
||||
function rect.Optimiser_new()
|
||||
return rect.Optimiser(native_dll.lib.rectangle_optimiser_new())
|
||||
end
|
||||
|
||||
return rect
|
||||
---Rectangle utilities.
|
||||
---@module 'noitapatcher.nsew.rect'
|
||||
|
||||
---@class Rect
|
||||
local rect = {}
|
||||
|
||||
local ffi = require("ffi")
|
||||
local native_dll = require("noitapatcher.nsew.native_dll")
|
||||
|
||||
ffi.cdef([[
|
||||
|
||||
struct nsew_rectangle {
|
||||
int32_t left;
|
||||
int32_t top;
|
||||
int32_t right;
|
||||
int32_t bottom;
|
||||
};
|
||||
|
||||
|
||||
struct nsew_rectangle_optimiser;
|
||||
|
||||
struct nsew_rectangle_optimiser* rectangle_optimiser_new();
|
||||
void rectangle_optimiser_delete(struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
void rectangle_optimiser_reset(struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
void rectangle_optimiser_submit(struct nsew_rectangle_optimiser* rectangle_optimiser, struct nsew_rectangle* rectangle);
|
||||
void rectangle_optimiser_scan(struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
int32_t rectangle_optimiser_size(const struct nsew_rectangle_optimiser* rectangle_optimiser);
|
||||
const struct nsew_rectangle* rectangle_optimiser_get(const struct nsew_rectangle_optimiser* rectangle_optimiser, int32_t index);
|
||||
|
||||
|
||||
struct lua_nsew_rectangle_optimiser {
|
||||
struct nsew_rectangle_optimiser* impl;
|
||||
};
|
||||
|
||||
]])
|
||||
|
||||
---@class Rectangle_fields
|
||||
---@field top integer
|
||||
---@field bottom integer
|
||||
---@field right integer
|
||||
---@field left integer
|
||||
|
||||
---@alias Rectangle Rectangle_mt | Rectangle_fields
|
||||
|
||||
---@class Optimiser_fields
|
||||
---@field top integer
|
||||
---@field bottom integer
|
||||
---@field right integer
|
||||
---@field left integer
|
||||
|
||||
---@alias Optimiser Optimiser_fields | Optimiser_mt
|
||||
|
||||
---@class Rectangle_mt
|
||||
local Rectangle_mt_index = {
|
||||
---@param r Rectangle
|
||||
---@return integer
|
||||
area = function(r)
|
||||
return (r.right - r.left) * (r.bottom - r.top)
|
||||
end,
|
||||
---@param r Rectangle
|
||||
---@return integer
|
||||
height = function(r)
|
||||
return r.bottom - r.top
|
||||
end,
|
||||
---@param r Rectangle
|
||||
---@return integer
|
||||
width = function(r)
|
||||
return r.right - r.left
|
||||
end,
|
||||
}
|
||||
local Rectangle_mt = {
|
||||
__index = Rectangle_mt_index,
|
||||
}
|
||||
|
||||
---@type fun(left, top, right, bottom): Rectangle
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
rect.Rectangle = ffi.metatype("struct nsew_rectangle", Rectangle_mt)
|
||||
|
||||
---Given an iterator that returns rectangles, return an iterator where the
|
||||
---rectangle extents never exceed `size`.
|
||||
---@param iterator fun(): Rectangle? returning rectangles
|
||||
---@param size integer maximum width and height
|
||||
---@return fun(): Rectangle? rectangles where the extents never exceed `size`
|
||||
function rect.parts(iterator, size)
|
||||
local region
|
||||
local posx
|
||||
local posy
|
||||
return function()
|
||||
if region == nil then
|
||||
region = iterator()
|
||||
if region == nil then
|
||||
return nil
|
||||
end
|
||||
posx = region.left
|
||||
posy = region.top
|
||||
end
|
||||
|
||||
local endx = math.min(posx + size, region.right)
|
||||
local endy = math.min(posy + size, region.bottom)
|
||||
|
||||
local ret = rect.Rectangle(posx, posy, endx, endy)
|
||||
|
||||
-- Setup for next iteration: place to the right, wraparound, or
|
||||
-- we're done with this region.
|
||||
if endx ~= region.right then
|
||||
posx = endx
|
||||
elseif endy ~= region.bottom then
|
||||
posx = region.left
|
||||
posy = endy
|
||||
else
|
||||
region = nil
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
---@class Optimiser_mt
|
||||
local Optimiser_mt_index = {
|
||||
submit = function(opt, rectangle)
|
||||
native_dll.lib.rectangle_optimiser_submit(opt.impl, rectangle)
|
||||
end,
|
||||
scan = function(opt)
|
||||
native_dll.lib.rectangle_optimiser_scan(opt.impl)
|
||||
end,
|
||||
reset = function(opt)
|
||||
native_dll.lib.rectangle_optimiser_reset(opt.impl)
|
||||
end,
|
||||
size = function(opt)
|
||||
return native_dll.lib.rectangle_optimiser_size(opt.impl)
|
||||
end,
|
||||
get = function(opt, index)
|
||||
return native_dll.lib.rectangle_optimiser_get(opt.impl, index)
|
||||
end,
|
||||
iterate = function(opt)
|
||||
local size = native_dll.lib.rectangle_optimiser_size(opt.impl)
|
||||
local index = 0
|
||||
return function()
|
||||
if index >= size then
|
||||
return nil
|
||||
end
|
||||
|
||||
local ret = native_dll.lib.rectangle_optimiser_get(opt.impl, index)
|
||||
index = index + 1
|
||||
return ret
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local Optimiser_mt = {
|
||||
__gc = function(opt)
|
||||
native_dll.lib.rectangle_optimiser_delete(opt.impl)
|
||||
end,
|
||||
__index = Optimiser_mt_index,
|
||||
}
|
||||
|
||||
---@type fun(unknown): Optimiser
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
rect.Optimiser = ffi.metatype("struct lua_nsew_rectangle_optimiser", Optimiser_mt)
|
||||
|
||||
---Create a new rectangle Optimiser
|
||||
---@return Optimiser optimiser
|
||||
function rect.Optimiser_new()
|
||||
return rect.Optimiser(native_dll.lib.rectangle_optimiser_new())
|
||||
end
|
||||
|
||||
return rect
|
||||
|
|
|
@ -1,272 +1,273 @@
|
|||
---@diagnostic disable: cast-local-type
|
||||
---World read / write functionality.
|
||||
---@module 'noitapatcher.nsew.world'
|
||||
---@class World
|
||||
local world = {}
|
||||
|
||||
local ffi = require("ffi")
|
||||
local world_ffi = require("noitapatcher.nsew.world_ffi")
|
||||
|
||||
local C = ffi.C
|
||||
|
||||
ffi.cdef([[
|
||||
|
||||
enum ENCODE_CONST {
|
||||
PIXEL_RUN_MAX = 4096,
|
||||
|
||||
LIQUID_FLAG_STATIC = 1,
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) EncodedAreaHeader {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
|
||||
uint16_t pixel_run_count;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) PixelRun {
|
||||
uint16_t length;
|
||||
int16_t material;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) EncodedArea {
|
||||
struct EncodedAreaHeader header;
|
||||
struct PixelRun pixel_runs[PIXEL_RUN_MAX];
|
||||
};
|
||||
|
||||
]])
|
||||
|
||||
---@class PixelRun
|
||||
---@field flags integer
|
||||
---@field material integer
|
||||
---@field length integer
|
||||
|
||||
---@class EncodedAreaHeader
|
||||
---@field x integer
|
||||
---@field y integer
|
||||
---@field width integer
|
||||
---@field height integer
|
||||
---@field pixel_run_count integer
|
||||
|
||||
---@class EncodedArea
|
||||
---@field header EncodedAreaHeader
|
||||
---@field pixel_runs PixelRun[] a pointer
|
||||
|
||||
world.EncodedAreaHeader = ffi.typeof("struct EncodedAreaHeader")
|
||||
world.PixelRun = ffi.typeof("struct PixelRun")
|
||||
---@type fun(): EncodedArea
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
world.EncodedArea = ffi.typeof("struct EncodedArea")
|
||||
|
||||
local pliquid_cell = ffi.typeof("struct CLiquidCell*")
|
||||
|
||||
---Total bytes taken up by the encoded area
|
||||
---@param encoded_area EncodedArea
|
||||
---@return integer total number of bytes that encodes the area
|
||||
---```lua
|
||||
---local data = ffi.string(area, world.encoded_size(area))
|
||||
---peer:send(data)
|
||||
---```
|
||||
function world.encoded_size(encoded_area)
|
||||
return ffi.sizeof(world.EncodedAreaHeader) + encoded_area.header.pixel_run_count * ffi.sizeof(world.PixelRun)
|
||||
end
|
||||
|
||||
---Encode the given rectangle of the world
|
||||
---The rectangle defined by {`start_x`, `start_y`, `end_x`, `end_y`} must not exceed 256 in width or height.
|
||||
---@param chunk_map unknown
|
||||
---@param start_x integer coordinate
|
||||
---@param start_y integer coordinate
|
||||
---@param end_x integer coordinate
|
||||
---@param end_y integer coordinate
|
||||
---@param encoded_area EncodedArea? memory to use, if nil this function allocates its own memory
|
||||
---@return EncodedArea? encoded_area returns an EncodedArea or nil if the area could not be encoded
|
||||
---@see decode
|
||||
function world.encode_area(chunk_map, start_x, start_y, end_x, end_y, encoded_area)
|
||||
start_x = ffi.cast('int32_t', start_x)
|
||||
start_y = ffi.cast('int32_t', start_y)
|
||||
end_x = ffi.cast('int32_t', end_x)
|
||||
end_y = ffi.cast('int32_t', end_y)
|
||||
---@cast start_x integer
|
||||
---@cast start_y integer
|
||||
---@cast end_x integer
|
||||
---@cast end_x integer
|
||||
|
||||
encoded_area = encoded_area or world.EncodedArea()
|
||||
|
||||
local width = end_x - start_x
|
||||
local height = end_y - start_y
|
||||
|
||||
if width <= 0 or height <= 0 then
|
||||
print("Invalid world part, negative dimension")
|
||||
return nil
|
||||
end
|
||||
|
||||
if width > 256 or height > 256 then
|
||||
print("Invalid world part, dimension greater than 256")
|
||||
return nil
|
||||
end
|
||||
|
||||
encoded_area.header.x = start_x
|
||||
encoded_area.header.y = start_y
|
||||
encoded_area.header.width = width - 1
|
||||
encoded_area.header.height = height - 1
|
||||
|
||||
local run_count = 1
|
||||
|
||||
local current_run = encoded_area.pixel_runs[0]
|
||||
local run_length = 0
|
||||
local current_material = 0
|
||||
local current_flags = 0
|
||||
|
||||
local y = start_y
|
||||
while y < end_y do
|
||||
local x = start_x
|
||||
while x < end_x do
|
||||
local material_number = 0
|
||||
local flags = 0
|
||||
|
||||
local ppixel = world_ffi.get_cell(chunk_map, x, y)
|
||||
local pixel = ppixel[0]
|
||||
if pixel ~= nil then
|
||||
local cell_type = pixel.vtable.get_cell_type(pixel)
|
||||
|
||||
if cell_type ~= C.CELL_TYPE_SOLID then
|
||||
local material_ptr = pixel.vtable.get_material(pixel)
|
||||
material_number = world_ffi.get_material_id(material_ptr)
|
||||
end
|
||||
|
||||
if cell_type == C.CELL_TYPE_LIQUID then
|
||||
local liquid_cell = ffi.cast(pliquid_cell, pixel)
|
||||
if liquid_cell.is_static then
|
||||
flags = bit.bor(flags, C.LIQUID_FLAG_STATIC)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if x == start_x and y == start_y then
|
||||
-- Initial run
|
||||
current_material = material_number
|
||||
current_flags = flags
|
||||
elseif current_material ~= material_number or current_flags ~= flags then
|
||||
-- Next run
|
||||
current_run.length = run_length - 1
|
||||
current_run.material = current_material
|
||||
current_run.flags = current_flags
|
||||
|
||||
if run_count == C.PIXEL_RUN_MAX then
|
||||
print("Area too complicated to encode")
|
||||
return nil
|
||||
end
|
||||
|
||||
current_run = encoded_area.pixel_runs[run_count]
|
||||
run_count = run_count + 1
|
||||
|
||||
run_length = 0
|
||||
current_material = material_number
|
||||
current_flags = flags
|
||||
end
|
||||
|
||||
run_length = run_length + 1
|
||||
|
||||
x = x + 1
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
current_run.length = run_length - 1
|
||||
current_run.material = current_material
|
||||
current_run.flags = current_flags
|
||||
|
||||
encoded_area.header.pixel_run_count = run_count
|
||||
|
||||
return encoded_area
|
||||
end
|
||||
|
||||
local PixelRun_const_ptr = ffi.typeof("struct PixelRun const*")
|
||||
|
||||
---Load an encoded area back into the world.
|
||||
---@param grid_world unknown
|
||||
---@param header EncodedAreaHeader header of the encoded area
|
||||
---@param pixel_runs PixelRun[] or ffi array of PixelRun from the encoded area
|
||||
---@see encode_area
|
||||
function world.decode(grid_world, header, pixel_runs)
|
||||
local chunk_map = grid_world.vtable.get_chunk_map(grid_world)
|
||||
|
||||
local top_left_x = header.x
|
||||
local top_left_y = header.y
|
||||
local width = header.width + 1
|
||||
local height = header.height + 1
|
||||
local bottom_right_x = top_left_x + width
|
||||
local bottom_right_y = top_left_y + height
|
||||
|
||||
local current_run_ix = 0
|
||||
local current_run = pixel_runs[current_run_ix]
|
||||
local new_material = current_run.material
|
||||
local flags = current_run.flags
|
||||
local left = current_run.length + 1
|
||||
|
||||
local y = top_left_y
|
||||
while y < bottom_right_y do
|
||||
local x = top_left_x
|
||||
while x < bottom_right_x do
|
||||
if world_ffi.chunk_loaded(chunk_map, x, y) then
|
||||
local ppixel = world_ffi.get_cell(chunk_map, x, y)
|
||||
local current_material = 0
|
||||
|
||||
if ppixel[0] ~= nil then
|
||||
local pixel = ppixel[0]
|
||||
current_material = world_ffi.get_material_id(pixel.vtable.get_material(pixel))
|
||||
|
||||
if new_material ~= current_material then
|
||||
world_ffi.remove_cell(grid_world, pixel, x, y, false)
|
||||
end
|
||||
end
|
||||
|
||||
if current_material ~= new_material and new_material ~= 0 then
|
||||
local pixel = world_ffi.construct_cell(grid_world, x, y, world_ffi.get_material_ptr(new_material), nil)
|
||||
if pixel == nil then
|
||||
-- TODO: This can happen when the material texture has a
|
||||
-- transparent pixel at the given coordinate. There's
|
||||
-- probably a better way to deal with this, but for now
|
||||
-- we skip positions like this.
|
||||
goto next_pixel
|
||||
end
|
||||
local cell_type = pixel.vtable.get_cell_type(pixel)
|
||||
|
||||
if cell_type == C.CELL_TYPE_LIQUID then
|
||||
local liquid_cell = ffi.cast(pliquid_cell, pixel)
|
||||
liquid_cell.is_static = bit.band(flags, C.CELL_TYPE_LIQUID) == C.LIQUID_FLAG_STATIC
|
||||
end
|
||||
|
||||
ppixel[0] = pixel
|
||||
end
|
||||
end
|
||||
|
||||
::next_pixel::
|
||||
|
||||
left = left - 1
|
||||
if left <= 0 then
|
||||
current_run_ix = current_run_ix + 1
|
||||
if current_run_ix >= header.pixel_run_count then
|
||||
-- No more runs, done
|
||||
assert(x == bottom_right_x - 1)
|
||||
assert(y == bottom_right_y - 1)
|
||||
return
|
||||
end
|
||||
|
||||
current_run = pixel_runs[current_run_ix]
|
||||
new_material = current_run.material
|
||||
flags = current_run.flags
|
||||
left = current_run.length + 1
|
||||
end
|
||||
|
||||
x = x + 1
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
|
||||
return world
|
||||
---@diagnostic disable: cast-local-type
|
||||
---World read / write functionality.
|
||||
---@module 'noitapatcher.nsew.world'
|
||||
---@class World
|
||||
local world = {}
|
||||
|
||||
local ffi = require("ffi")
|
||||
local world_ffi = require("noitapatcher.nsew.world_ffi")
|
||||
|
||||
local C = ffi.C
|
||||
|
||||
ffi.cdef([[
|
||||
|
||||
enum ENCODE_CONST {
|
||||
PIXEL_RUN_MAX = 4096,
|
||||
|
||||
LIQUID_FLAG_STATIC = 1,
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) EncodedAreaHeader {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
|
||||
uint16_t pixel_run_count;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) PixelRun {
|
||||
uint16_t length;
|
||||
int16_t material;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) EncodedArea {
|
||||
struct EncodedAreaHeader header;
|
||||
struct PixelRun pixel_runs[PIXEL_RUN_MAX];
|
||||
};
|
||||
|
||||
]])
|
||||
|
||||
---@class PixelRun
|
||||
---@field flags integer
|
||||
---@field material integer
|
||||
---@field length integer
|
||||
|
||||
---@class EncodedAreaHeader
|
||||
---@field x integer
|
||||
---@field y integer
|
||||
---@field width integer
|
||||
---@field height integer
|
||||
---@field pixel_run_count integer
|
||||
|
||||
---@class EncodedArea
|
||||
---@field header EncodedAreaHeader
|
||||
---@field pixel_runs PixelRun[] a pointer
|
||||
|
||||
world.EncodedAreaHeader = ffi.typeof("struct EncodedAreaHeader")
|
||||
world.PixelRun = ffi.typeof("struct PixelRun")
|
||||
---@type fun(): EncodedArea
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
world.EncodedArea = ffi.typeof("struct EncodedArea")
|
||||
|
||||
local pliquid_cell = ffi.typeof("struct CLiquidCell*")
|
||||
|
||||
---Total bytes taken up by the encoded area
|
||||
---@param encoded_area EncodedArea
|
||||
---@return integer total number of bytes that encodes the area
|
||||
---```lua
|
||||
---local data = ffi.string(area, world.encoded_size(area))
|
||||
---peer:send(data)
|
||||
---```
|
||||
function world.encoded_size(encoded_area)
|
||||
return ffi.sizeof(world.EncodedAreaHeader) + encoded_area.header.pixel_run_count * ffi.sizeof(world.PixelRun)
|
||||
end
|
||||
|
||||
---Encode the given rectangle of the world
|
||||
---The rectangle defined by {`start_x`, `start_y`, `end_x`, `end_y`} must not exceed 256 in width or height.
|
||||
---@param chunk_map unknown
|
||||
---@param start_x integer coordinate
|
||||
---@param start_y integer coordinate
|
||||
---@param end_x integer coordinate
|
||||
---@param end_y integer coordinate
|
||||
---@param encoded_area EncodedArea? memory to use, if nil this function allocates its own memory
|
||||
---@return EncodedArea? encoded_area returns an EncodedArea or nil if the area could not be encoded
|
||||
---@see decode
|
||||
function world.encode_area(chunk_map, start_x, start_y, end_x, end_y, encoded_area)
|
||||
start_x = ffi.cast("int32_t", start_x)
|
||||
start_y = ffi.cast("int32_t", start_y)
|
||||
end_x = ffi.cast("int32_t", end_x)
|
||||
end_y = ffi.cast("int32_t", end_y)
|
||||
---@cast start_x integer
|
||||
---@cast start_y integer
|
||||
---@cast end_x integer
|
||||
---@cast end_x integer
|
||||
|
||||
encoded_area = encoded_area or world.EncodedArea()
|
||||
|
||||
local width = end_x - start_x
|
||||
local height = end_y - start_y
|
||||
|
||||
if width <= 0 or height <= 0 then
|
||||
print("Invalid world part, negative dimension")
|
||||
return nil
|
||||
end
|
||||
|
||||
if width > 256 or height > 256 then
|
||||
print("Invalid world part, dimension greater than 256")
|
||||
return nil
|
||||
end
|
||||
|
||||
encoded_area.header.x = start_x
|
||||
encoded_area.header.y = start_y
|
||||
encoded_area.header.width = width - 1
|
||||
encoded_area.header.height = height - 1
|
||||
|
||||
local run_count = 1
|
||||
|
||||
local current_run = encoded_area.pixel_runs[0]
|
||||
local run_length = 0
|
||||
local current_material = 0
|
||||
local current_flags = 0
|
||||
|
||||
local y = start_y
|
||||
while y < end_y do
|
||||
local x = start_x
|
||||
while x < end_x do
|
||||
local material_number = 0
|
||||
local flags = 0
|
||||
|
||||
local ppixel = world_ffi.get_cell(chunk_map, x, y)
|
||||
local pixel = ppixel[0]
|
||||
if pixel ~= nil then
|
||||
local cell_type = pixel.vtable.get_cell_type(pixel)
|
||||
|
||||
if cell_type ~= C.CELL_TYPE_SOLID then
|
||||
local material_ptr = pixel.vtable.get_material(pixel)
|
||||
material_number = world_ffi.get_material_id(material_ptr)
|
||||
end
|
||||
|
||||
if cell_type == C.CELL_TYPE_LIQUID then
|
||||
local liquid_cell = ffi.cast(pliquid_cell, pixel)
|
||||
if liquid_cell.is_static then
|
||||
flags = bit.bor(flags, C.LIQUID_FLAG_STATIC)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if x == start_x and y == start_y then
|
||||
-- Initial run
|
||||
current_material = material_number
|
||||
current_flags = flags
|
||||
elseif current_material ~= material_number or current_flags ~= flags then
|
||||
-- Next run
|
||||
current_run.length = run_length - 1
|
||||
current_run.material = current_material
|
||||
current_run.flags = current_flags
|
||||
|
||||
if run_count == C.PIXEL_RUN_MAX then
|
||||
print("Area too complicated to encode")
|
||||
return nil
|
||||
end
|
||||
|
||||
current_run = encoded_area.pixel_runs[run_count]
|
||||
run_count = run_count + 1
|
||||
|
||||
run_length = 0
|
||||
current_material = material_number
|
||||
current_flags = flags
|
||||
end
|
||||
|
||||
run_length = run_length + 1
|
||||
|
||||
x = x + 1
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
current_run.length = run_length - 1
|
||||
current_run.material = current_material
|
||||
current_run.flags = current_flags
|
||||
|
||||
encoded_area.header.pixel_run_count = run_count
|
||||
|
||||
return encoded_area
|
||||
end
|
||||
|
||||
local PixelRun_const_ptr = ffi.typeof("struct PixelRun const*")
|
||||
|
||||
---Load an encoded area back into the world.
|
||||
---@param grid_world unknown
|
||||
---@param header EncodedAreaHeader header of the encoded area
|
||||
---@param pixel_runs PixelRun[] or ffi array of PixelRun from the encoded area
|
||||
---@see encode_area
|
||||
function world.decode(grid_world, header, pixel_runs)
|
||||
local chunk_map = grid_world.vtable.get_chunk_map(grid_world)
|
||||
|
||||
local top_left_x = header.x
|
||||
local top_left_y = header.y
|
||||
local width = header.width + 1
|
||||
local height = header.height + 1
|
||||
local bottom_right_x = top_left_x + width
|
||||
local bottom_right_y = top_left_y + height
|
||||
|
||||
local current_run_ix = 0
|
||||
local current_run = pixel_runs[current_run_ix]
|
||||
local new_material = current_run.material
|
||||
local flags = current_run.flags
|
||||
local left = current_run.length + 1
|
||||
|
||||
local y = top_left_y
|
||||
while y < bottom_right_y do
|
||||
local x = top_left_x
|
||||
while x < bottom_right_x do
|
||||
if world_ffi.chunk_loaded(chunk_map, x, y) then
|
||||
local ppixel = world_ffi.get_cell(chunk_map, x, y)
|
||||
local current_material = 0
|
||||
|
||||
if ppixel[0] ~= nil then
|
||||
local pixel = ppixel[0]
|
||||
current_material = world_ffi.get_material_id(pixel.vtable.get_material(pixel))
|
||||
|
||||
if new_material ~= current_material then
|
||||
world_ffi.remove_cell(grid_world, pixel, x, y, false)
|
||||
end
|
||||
end
|
||||
|
||||
if current_material ~= new_material and new_material ~= 0 then
|
||||
local pixel =
|
||||
world_ffi.construct_cell(grid_world, x, y, world_ffi.get_material_ptr(new_material), nil)
|
||||
if pixel == nil then
|
||||
-- TODO: This can happen when the material texture has a
|
||||
-- transparent pixel at the given coordinate. There's
|
||||
-- probably a better way to deal with this, but for now
|
||||
-- we skip positions like this.
|
||||
goto next_pixel
|
||||
end
|
||||
local cell_type = pixel.vtable.get_cell_type(pixel)
|
||||
|
||||
if cell_type == C.CELL_TYPE_LIQUID then
|
||||
local liquid_cell = ffi.cast(pliquid_cell, pixel)
|
||||
liquid_cell.is_static = bit.band(flags, C.CELL_TYPE_LIQUID) == C.LIQUID_FLAG_STATIC
|
||||
end
|
||||
|
||||
ppixel[0] = pixel
|
||||
end
|
||||
end
|
||||
|
||||
::next_pixel::
|
||||
|
||||
left = left - 1
|
||||
if left <= 0 then
|
||||
current_run_ix = current_run_ix + 1
|
||||
if current_run_ix >= header.pixel_run_count then
|
||||
-- No more runs, done
|
||||
assert(x == bottom_right_x - 1)
|
||||
assert(y == bottom_right_y - 1)
|
||||
return
|
||||
end
|
||||
|
||||
current_run = pixel_runs[current_run_ix]
|
||||
new_material = current_run.material
|
||||
flags = current_run.flags
|
||||
left = current_run.length + 1
|
||||
end
|
||||
|
||||
x = x + 1
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
|
||||
return world
|
||||
|
|
|
@ -1,425 +1,425 @@
|
|||
---@diagnostic disable: assign-type-mismatch
|
||||
---Noita world functionality exposed.
|
||||
---@module 'noitapatcher.nsew.world_ffi'
|
||||
|
||||
---@class WorldFFI
|
||||
local world_ffi = {}
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
local np = require("noitapatcher")
|
||||
local world_info = np.GetWorldInfo()
|
||||
|
||||
if not world_info then
|
||||
error("Couldn't get world info from NoitaPatcher.")
|
||||
end
|
||||
|
||||
local gg_ptr = world_info.game_global
|
||||
|
||||
ffi.cdef([[
|
||||
|
||||
typedef void* __thiscall placeholder_memfn(void*);
|
||||
|
||||
struct Position {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct Colour {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
struct AABB {
|
||||
struct Position top_left;
|
||||
struct Position bottom_right;
|
||||
};
|
||||
|
||||
struct std_string { /* VC++ std::string */
|
||||
char *buffer;
|
||||
char sso_buffer[12];
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
typedef enum cell_type {
|
||||
none=0,
|
||||
liquid=1,
|
||||
gas=2,
|
||||
solid=3,
|
||||
fire=4,
|
||||
invalid=4294967295
|
||||
} cell_type;
|
||||
|
||||
struct CellData {
|
||||
struct std_string name;
|
||||
struct std_string ui_name;
|
||||
int material_type;
|
||||
int id_2;
|
||||
enum cell_type cell_type;
|
||||
int platform_type;
|
||||
unsigned int wang_color;
|
||||
int gfx_glow;
|
||||
unsigned int gfx_glow_color;
|
||||
char unknown1[24];
|
||||
unsigned int default_primary_colour;
|
||||
char unknown2[36];
|
||||
bool cell_holes_in_texture;
|
||||
bool stainable;
|
||||
bool burnable;
|
||||
bool on_fire;
|
||||
int fire_hp;
|
||||
int autoignition_temperature;
|
||||
int _100_minus_autoignition_temp;
|
||||
int temperature_of_fire;
|
||||
int generates_smoke;
|
||||
int generates_flames;
|
||||
bool requires_oxygen;
|
||||
char padding1[3];
|
||||
struct std_string on_fire_convert_to_material;
|
||||
int on_fire_convert_to_material_id;
|
||||
struct std_string on_fire_flame_material;
|
||||
int on_fire_flame_material_id;
|
||||
struct std_string on_fire_smoke_material;
|
||||
int on_fire_smoke_material_id;
|
||||
struct ConfigExplosion *explosion_config;
|
||||
int durability;
|
||||
int crackability;
|
||||
bool electrical_conductivity;
|
||||
bool slippery;
|
||||
char padding2[2];
|
||||
float stickyness;
|
||||
struct std_string cold_freezes_to_material;
|
||||
struct std_string warmth_melts_to_material;
|
||||
int warmth_melts_to_material_id;
|
||||
int cold_freezes_to_material_id;
|
||||
int16_t cold_freezes_chance_rev;
|
||||
int16_t warmth_melts_chance_rev;
|
||||
bool cold_freezes_to_dont_do_reverse_reaction;
|
||||
char padding3[3];
|
||||
int lifetime;
|
||||
int hp;
|
||||
float density;
|
||||
bool liquid_sand;
|
||||
bool liquid_slime;
|
||||
bool liquid_static;
|
||||
bool liquid_stains_self;
|
||||
int liquid_sticks_to_ceiling;
|
||||
float liquid_gravity;
|
||||
int liquid_viscosity;
|
||||
int liquid_stains;
|
||||
unsigned int liquid_stains_custom_color;
|
||||
float liquid_sprite_stain_shaken_drop_chance;
|
||||
float liquid_sprite_stain_ignited_drop_chance;
|
||||
int8_t liquid_sprite_stains_check_offset;
|
||||
char padding4[3];
|
||||
float liquid_sprite_stains_status_threshold;
|
||||
float liquid_damping;
|
||||
float liquid_flow_speed;
|
||||
bool liquid_sand_never_box2d;
|
||||
char unknown7[3];
|
||||
int8_t gas_speed;
|
||||
int8_t gas_upwards_speed;
|
||||
int8_t gas_horizontal_speed;
|
||||
int8_t gas_downwards_speed;
|
||||
float solid_friction;
|
||||
float solid_restitution;
|
||||
float solid_gravity_scale;
|
||||
int solid_static_type;
|
||||
float solid_on_collision_splash_power;
|
||||
bool solid_on_collision_explode;
|
||||
bool solid_on_sleep_convert;
|
||||
bool solid_on_collision_convert;
|
||||
bool solid_on_break_explode;
|
||||
bool solid_go_through_sand;
|
||||
bool solid_collide_with_self;
|
||||
char padding5[2];
|
||||
struct std_string solid_on_collision_material;
|
||||
int solid_on_collision_material_id;
|
||||
struct std_string solid_break_to_type;
|
||||
int solid_break_to_type_id;
|
||||
struct std_string convert_to_box2d_material;
|
||||
int convert_to_box2d_material_id;
|
||||
int vegetation_full_lifetime_growth;
|
||||
struct std_string vegetation_sprite;
|
||||
bool vegetation_random_flip_x_scale;
|
||||
char padding6[3];
|
||||
char unknown11[12];
|
||||
float wang_noise_percent;
|
||||
float wang_curvature;
|
||||
int wang_noise_type;
|
||||
char unknown12[12];
|
||||
bool danger_fire;
|
||||
bool danger_radioactive;
|
||||
bool danger_poison;
|
||||
bool danger_water;
|
||||
char unknown13[24];
|
||||
bool always_ignites_damagemodel;
|
||||
bool ignore_self_reaction_warning;
|
||||
char padding7[2];
|
||||
char unknown14[12];
|
||||
float audio_size_multiplier;
|
||||
bool audio_is_soft;
|
||||
char padding8[3];
|
||||
char unknown15[8];
|
||||
bool show_in_creative_mode;
|
||||
bool is_just_particle_fx;
|
||||
char padding9[2];
|
||||
// struct grid_CosmeticParticleConfig *ParticleEffect;
|
||||
};
|
||||
|
||||
enum CellType {
|
||||
CELL_TYPE_NONE = 0,
|
||||
CELL_TYPE_LIQUID = 1,
|
||||
CELL_TYPE_GAS = 2,
|
||||
CELL_TYPE_SOLID = 3,
|
||||
CELL_TYPE_FIRE = 4,
|
||||
};
|
||||
|
||||
struct Cell_vtable {
|
||||
void (__thiscall *destroy)(struct Cell*, char dealloc);
|
||||
enum CellType (__thiscall *get_cell_type)(struct Cell*);
|
||||
void* field2_0x8;
|
||||
void* field3_0xc;
|
||||
void* field4_0x10;
|
||||
struct Colour (__thiscall *get_colour)(struct Cell*);
|
||||
void* field6_0x18;
|
||||
void (__thiscall *set_colour)(struct Cell*, struct Colour);
|
||||
void* field8_0x20;
|
||||
void* field9_0x24;
|
||||
void* field10_0x28;
|
||||
void* field11_0x2c;
|
||||
struct CellData* (__thiscall *get_material)(void *);
|
||||
void* field13_0x34;
|
||||
void* field14_0x38;
|
||||
void* field15_0x3c;
|
||||
void* field16_0x40;
|
||||
void* field17_0x44;
|
||||
void* field18_0x48;
|
||||
void* field19_0x4c;
|
||||
struct Position * (__thiscall *get_position)(void *, struct Position *);
|
||||
void* field21_0x54;
|
||||
void* field22_0x58;
|
||||
void* field23_0x5c;
|
||||
void* field24_0x60;
|
||||
void* field25_0x64;
|
||||
void* field26_0x68;
|
||||
void* field27_0x6c;
|
||||
void* field28_0x70;
|
||||
bool (__thiscall *is_burning)(struct Cell*);
|
||||
void* field30_0x78;
|
||||
void* field31_0x7c;
|
||||
void* field32_0x80;
|
||||
void (__thiscall *stop_burning)(struct Cell*);
|
||||
void* field34_0x88;
|
||||
void* field35_0x8c;
|
||||
void* field36_0x90;
|
||||
void* field37_0x94;
|
||||
void* field38_0x98;
|
||||
void (__thiscall *remove)(struct Cell*);
|
||||
void* field40_0xa0;
|
||||
};
|
||||
|
||||
// In the Noita code this would be the ICellBurnable class
|
||||
struct Cell {
|
||||
struct Cell_vtable* vtable;
|
||||
|
||||
int hp;
|
||||
char unknown1[8];
|
||||
bool is_burning;
|
||||
char unknown2[3];
|
||||
uintptr_t material_ptr;
|
||||
};
|
||||
|
||||
struct CLiquidCell {
|
||||
struct Cell cell;
|
||||
int x;
|
||||
int y;
|
||||
char unknown1;
|
||||
char unknown2;
|
||||
bool is_static;
|
||||
char unknown3;
|
||||
int unknown4[3];
|
||||
struct Colour colour;
|
||||
unsigned not_colour;
|
||||
};
|
||||
|
||||
typedef struct Cell (*cell_array)[0x40000];
|
||||
|
||||
struct ChunkMap {
|
||||
int unknown[2];
|
||||
cell_array* (*cells)[0x40000];
|
||||
int unknown2[8];
|
||||
};
|
||||
|
||||
struct GridWorld_vtable {
|
||||
placeholder_memfn* unknown[3];
|
||||
struct ChunkMap* (__thiscall *get_chunk_map)(struct GridWorld* this);
|
||||
placeholder_memfn* unknown2[30];
|
||||
};
|
||||
|
||||
struct GridWorld {
|
||||
struct GridWorld_vtable* vtable;
|
||||
int unknown[318];
|
||||
int world_update_count;
|
||||
struct ChunkMap chunk_map;
|
||||
int unknown2[41];
|
||||
struct GridWorldThreadImpl* mThreadImpl;
|
||||
};
|
||||
|
||||
struct GridWorldThreaded_vtable;
|
||||
|
||||
struct GridWorldThreaded {
|
||||
struct GridWorldThreaded_vtable* vtable;
|
||||
int unknown[287];
|
||||
struct AABB update_region;
|
||||
};
|
||||
|
||||
struct vec_pGridWorldThreaded {
|
||||
struct GridWorldThreaded** begin;
|
||||
struct GridWorldThreaded** end_;
|
||||
struct GridWorldThreaded** capacity_end;
|
||||
};
|
||||
|
||||
struct WorldUpdateParams {
|
||||
struct AABB update_region;
|
||||
int unknown;
|
||||
struct GridWorldThreaded* grid_world_threaded;
|
||||
};
|
||||
|
||||
struct vec_WorldUpdateParams {
|
||||
struct WorldUpdateParams* begin;
|
||||
struct WorldUpdateParams* end_;
|
||||
struct WorldUpdateParams* capacity_end;
|
||||
};
|
||||
|
||||
struct GridWorldThreadImpl {
|
||||
int chunk_update_count;
|
||||
struct vec_pGridWorldThreaded updated_grid_worlds;
|
||||
|
||||
int world_update_params_count;
|
||||
struct vec_WorldUpdateParams world_update_params;
|
||||
|
||||
int grid_with_area_count;
|
||||
struct vec_pGridWorldThreaded with_area_grid_worlds;
|
||||
|
||||
int another_count;
|
||||
int another_vec[3];
|
||||
|
||||
int some_kind_of_ptr;
|
||||
int some_kind_of_counter;
|
||||
|
||||
int last_vec[3];
|
||||
};
|
||||
|
||||
typedef struct Cell** __thiscall get_cell_f(struct ChunkMap*, int x, int y);
|
||||
typedef bool __thiscall chunk_loaded_f(struct ChunkMap*, int x, int y);
|
||||
|
||||
typedef void __thiscall remove_cell_f(struct GridWorld*, void* cell, int x, int y, bool);
|
||||
typedef struct Cell* __thiscall construct_cell_f(struct GridWorld*, int x, int y, void* material_ptr, void* memory);
|
||||
|
||||
]])
|
||||
|
||||
--local function check_celldata_field(f, o)
|
||||
-- local offset = ffi.offsetof("struct CellData", f)
|
||||
-- assert(offset == o, "Expected field " .. f .. " to be at offset " .. o)
|
||||
--end
|
||||
--
|
||||
--check_celldata_field("wang_color", 0x40)
|
||||
--check_celldata_field("generates_flames", 0xa4)
|
||||
--check_celldata_field("durability", 0x104)
|
||||
--check_celldata_field("cold_freezes_to_material", 0x114)
|
||||
--check_celldata_field("liquid_sand", 0x160)
|
||||
--check_celldata_field("liquid_sprite_stain_ignited_drop_chance", 0x17c)
|
||||
--check_celldata_field("gas_horizontal_speed", 0x196)
|
||||
--check_celldata_field("solid_on_sleep_convert", 0x1ad)
|
||||
--check_celldata_field("solid_break_to_type", 0x1d0)
|
||||
--check_celldata_field("vegetation_sprite", 0x20c)
|
||||
--check_celldata_field("wang_noise_type", 0x23c)
|
||||
--check_celldata_field("ignore_self_reaction_warning", 0x269)
|
||||
--check_celldata_field("is_just_particle_fx", 0x289)
|
||||
|
||||
---@class ChunkMap pointer type
|
||||
---@class GridWorld pointer type
|
||||
---@class CellData pointer type
|
||||
---@class Cell pointer type
|
||||
|
||||
---Access a pixel in the world.
|
||||
---You can write a cell created from world_ffi.construct_cell to this pointer to add a cell into the world.
|
||||
---If there's already a cell at this position, make sure to call world_ffi.remove_cell first.
|
||||
---@type fun(chunk_map: ChunkMap, x: integer, y: integer): Cell
|
||||
world_ffi.get_cell = ffi.cast("get_cell_f*", world_info.get_cell)
|
||||
|
||||
---Remove a cell from the world. bool return has unknown meaning.
|
||||
---@type fun(grid_world: GridWorld, cell: Cell, x: integer, y: integer): boolean
|
||||
world_ffi.remove_cell = ffi.cast("remove_cell_f*", world_info.remove_cell)
|
||||
|
||||
---Create a new cell. If memory is null pointer it will allocate its own memory.
|
||||
---@type fun(grid_world: GridWorld, x: integer, y: integer, material: CellData, memory: ffi.cdata*)
|
||||
world_ffi.construct_cell = ffi.cast("construct_cell_f*", world_info.construct_cell)
|
||||
|
||||
---Check if a chunk is loaded. x and y are world coordinates.
|
||||
---```lua
|
||||
---if world_ffi.chunk_loaded(chunk_map, x, y) then
|
||||
--- local cell = world_ffi.get_cell(chunk_map, x, y)
|
||||
--- ..
|
||||
---```
|
||||
---@type fun(chunk_map: ChunkMap, x: integer, y: integer): boolean
|
||||
world_ffi.chunk_loaded = ffi.cast("chunk_loaded_f*", world_info.chunk_loaded)
|
||||
|
||||
world_ffi.Position = ffi.typeof("struct Position")
|
||||
world_ffi.Colour = ffi.typeof("struct Colour")
|
||||
world_ffi.AABB = ffi.typeof("struct AABB")
|
||||
world_ffi.CellType = ffi.typeof("enum CellType")
|
||||
world_ffi.Cell = ffi.typeof("struct Cell")
|
||||
world_ffi.CLiquidCell = ffi.typeof("struct CLiquidCell")
|
||||
world_ffi.ChunkMap = ffi.typeof("struct ChunkMap")
|
||||
world_ffi.GridWorld = ffi.typeof("struct GridWorld")
|
||||
world_ffi.GridWorldThreaded = ffi.typeof("struct GridWorldThreaded")
|
||||
world_ffi.WorldUpdateParams = ffi.typeof("struct WorldUpdateParams")
|
||||
world_ffi.GridWorldThreadImpl = ffi.typeof("struct GridWorldThreadImpl")
|
||||
|
||||
---Get the grid world.
|
||||
---@return GridWorld
|
||||
function world_ffi.get_grid_world()
|
||||
local game_global = ffi.cast("void*", gg_ptr)
|
||||
local world_data = ffi.cast("void**", ffi.cast("char*", game_global) + 0xc)[0]
|
||||
local grid_world = ffi.cast("struct GridWorld**", ffi.cast("char*", world_data) + 0x44)[0]
|
||||
return grid_world
|
||||
end
|
||||
|
||||
local celldata_size = 0x290
|
||||
local CellData_ptr = ffi.typeof("struct CellData*")
|
||||
|
||||
---Turn a standard material id into a material pointer.
|
||||
---@param id integer material id that is used in the standard Noita functions
|
||||
---@return CellData material to internal material data (aka cell data).
|
||||
---```lua
|
||||
---local gold_ptr = world_ffi.get_material_ptr(CellFactory_GetType("gold"))
|
||||
---```
|
||||
function world_ffi.get_material_ptr(id)
|
||||
local game_global = ffi.cast("char*", gg_ptr)
|
||||
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
|
||||
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
|
||||
local ptr = begin + celldata_size * id
|
||||
return ffi.cast(CellData_ptr, ptr) --[[@as CellData]]
|
||||
end
|
||||
|
||||
---Turn a material pointer into a standard material id.
|
||||
---@param material CellData to a material (aka cell data)
|
||||
---@return integer material id that is accepted by standard Noita functions such as `CellFactory_GetUIName` and `ConvertMaterialOnAreaInstantly`.
|
||||
---```lua
|
||||
---local mat_id = world_ffi.get_material_id(cell.vtable.get_material(cell))
|
||||
---```
|
||||
---See: `world_ffi.get_material_ptr`
|
||||
function world_ffi.get_material_id(material)
|
||||
local game_global = ffi.cast("char*", gg_ptr)
|
||||
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
|
||||
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
|
||||
local offset = ffi.cast('char*', material) - begin
|
||||
return offset / celldata_size
|
||||
end
|
||||
|
||||
return world_ffi
|
||||
---@diagnostic disable: assign-type-mismatch
|
||||
---Noita world functionality exposed.
|
||||
---@module 'noitapatcher.nsew.world_ffi'
|
||||
|
||||
---@class WorldFFI
|
||||
local world_ffi = {}
|
||||
|
||||
local ffi = require("ffi")
|
||||
|
||||
local np = require("noitapatcher")
|
||||
local world_info = np.GetWorldInfo()
|
||||
|
||||
if not world_info then
|
||||
error("Couldn't get world info from NoitaPatcher.")
|
||||
end
|
||||
|
||||
local gg_ptr = world_info.game_global
|
||||
|
||||
ffi.cdef([[
|
||||
|
||||
typedef void* __thiscall placeholder_memfn(void*);
|
||||
|
||||
struct Position {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct Colour {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
struct AABB {
|
||||
struct Position top_left;
|
||||
struct Position bottom_right;
|
||||
};
|
||||
|
||||
struct std_string { /* VC++ std::string */
|
||||
char *buffer;
|
||||
char sso_buffer[12];
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
typedef enum cell_type {
|
||||
none=0,
|
||||
liquid=1,
|
||||
gas=2,
|
||||
solid=3,
|
||||
fire=4,
|
||||
invalid=4294967295
|
||||
} cell_type;
|
||||
|
||||
struct CellData {
|
||||
struct std_string name;
|
||||
struct std_string ui_name;
|
||||
int material_type;
|
||||
int id_2;
|
||||
enum cell_type cell_type;
|
||||
int platform_type;
|
||||
unsigned int wang_color;
|
||||
int gfx_glow;
|
||||
unsigned int gfx_glow_color;
|
||||
char unknown1[24];
|
||||
unsigned int default_primary_colour;
|
||||
char unknown2[36];
|
||||
bool cell_holes_in_texture;
|
||||
bool stainable;
|
||||
bool burnable;
|
||||
bool on_fire;
|
||||
int fire_hp;
|
||||
int autoignition_temperature;
|
||||
int _100_minus_autoignition_temp;
|
||||
int temperature_of_fire;
|
||||
int generates_smoke;
|
||||
int generates_flames;
|
||||
bool requires_oxygen;
|
||||
char padding1[3];
|
||||
struct std_string on_fire_convert_to_material;
|
||||
int on_fire_convert_to_material_id;
|
||||
struct std_string on_fire_flame_material;
|
||||
int on_fire_flame_material_id;
|
||||
struct std_string on_fire_smoke_material;
|
||||
int on_fire_smoke_material_id;
|
||||
struct ConfigExplosion *explosion_config;
|
||||
int durability;
|
||||
int crackability;
|
||||
bool electrical_conductivity;
|
||||
bool slippery;
|
||||
char padding2[2];
|
||||
float stickyness;
|
||||
struct std_string cold_freezes_to_material;
|
||||
struct std_string warmth_melts_to_material;
|
||||
int warmth_melts_to_material_id;
|
||||
int cold_freezes_to_material_id;
|
||||
int16_t cold_freezes_chance_rev;
|
||||
int16_t warmth_melts_chance_rev;
|
||||
bool cold_freezes_to_dont_do_reverse_reaction;
|
||||
char padding3[3];
|
||||
int lifetime;
|
||||
int hp;
|
||||
float density;
|
||||
bool liquid_sand;
|
||||
bool liquid_slime;
|
||||
bool liquid_static;
|
||||
bool liquid_stains_self;
|
||||
int liquid_sticks_to_ceiling;
|
||||
float liquid_gravity;
|
||||
int liquid_viscosity;
|
||||
int liquid_stains;
|
||||
unsigned int liquid_stains_custom_color;
|
||||
float liquid_sprite_stain_shaken_drop_chance;
|
||||
float liquid_sprite_stain_ignited_drop_chance;
|
||||
int8_t liquid_sprite_stains_check_offset;
|
||||
char padding4[3];
|
||||
float liquid_sprite_stains_status_threshold;
|
||||
float liquid_damping;
|
||||
float liquid_flow_speed;
|
||||
bool liquid_sand_never_box2d;
|
||||
char unknown7[3];
|
||||
int8_t gas_speed;
|
||||
int8_t gas_upwards_speed;
|
||||
int8_t gas_horizontal_speed;
|
||||
int8_t gas_downwards_speed;
|
||||
float solid_friction;
|
||||
float solid_restitution;
|
||||
float solid_gravity_scale;
|
||||
int solid_static_type;
|
||||
float solid_on_collision_splash_power;
|
||||
bool solid_on_collision_explode;
|
||||
bool solid_on_sleep_convert;
|
||||
bool solid_on_collision_convert;
|
||||
bool solid_on_break_explode;
|
||||
bool solid_go_through_sand;
|
||||
bool solid_collide_with_self;
|
||||
char padding5[2];
|
||||
struct std_string solid_on_collision_material;
|
||||
int solid_on_collision_material_id;
|
||||
struct std_string solid_break_to_type;
|
||||
int solid_break_to_type_id;
|
||||
struct std_string convert_to_box2d_material;
|
||||
int convert_to_box2d_material_id;
|
||||
int vegetation_full_lifetime_growth;
|
||||
struct std_string vegetation_sprite;
|
||||
bool vegetation_random_flip_x_scale;
|
||||
char padding6[3];
|
||||
char unknown11[12];
|
||||
float wang_noise_percent;
|
||||
float wang_curvature;
|
||||
int wang_noise_type;
|
||||
char unknown12[12];
|
||||
bool danger_fire;
|
||||
bool danger_radioactive;
|
||||
bool danger_poison;
|
||||
bool danger_water;
|
||||
char unknown13[24];
|
||||
bool always_ignites_damagemodel;
|
||||
bool ignore_self_reaction_warning;
|
||||
char padding7[2];
|
||||
char unknown14[12];
|
||||
float audio_size_multiplier;
|
||||
bool audio_is_soft;
|
||||
char padding8[3];
|
||||
char unknown15[8];
|
||||
bool show_in_creative_mode;
|
||||
bool is_just_particle_fx;
|
||||
char padding9[2];
|
||||
// struct grid_CosmeticParticleConfig *ParticleEffect;
|
||||
};
|
||||
|
||||
enum CellType {
|
||||
CELL_TYPE_NONE = 0,
|
||||
CELL_TYPE_LIQUID = 1,
|
||||
CELL_TYPE_GAS = 2,
|
||||
CELL_TYPE_SOLID = 3,
|
||||
CELL_TYPE_FIRE = 4,
|
||||
};
|
||||
|
||||
struct Cell_vtable {
|
||||
void (__thiscall *destroy)(struct Cell*, char dealloc);
|
||||
enum CellType (__thiscall *get_cell_type)(struct Cell*);
|
||||
void* field2_0x8;
|
||||
void* field3_0xc;
|
||||
void* field4_0x10;
|
||||
struct Colour (__thiscall *get_colour)(struct Cell*);
|
||||
void* field6_0x18;
|
||||
void (__thiscall *set_colour)(struct Cell*, struct Colour);
|
||||
void* field8_0x20;
|
||||
void* field9_0x24;
|
||||
void* field10_0x28;
|
||||
void* field11_0x2c;
|
||||
struct CellData* (__thiscall *get_material)(void *);
|
||||
void* field13_0x34;
|
||||
void* field14_0x38;
|
||||
void* field15_0x3c;
|
||||
void* field16_0x40;
|
||||
void* field17_0x44;
|
||||
void* field18_0x48;
|
||||
void* field19_0x4c;
|
||||
struct Position * (__thiscall *get_position)(void *, struct Position *);
|
||||
void* field21_0x54;
|
||||
void* field22_0x58;
|
||||
void* field23_0x5c;
|
||||
void* field24_0x60;
|
||||
void* field25_0x64;
|
||||
void* field26_0x68;
|
||||
void* field27_0x6c;
|
||||
void* field28_0x70;
|
||||
bool (__thiscall *is_burning)(struct Cell*);
|
||||
void* field30_0x78;
|
||||
void* field31_0x7c;
|
||||
void* field32_0x80;
|
||||
void (__thiscall *stop_burning)(struct Cell*);
|
||||
void* field34_0x88;
|
||||
void* field35_0x8c;
|
||||
void* field36_0x90;
|
||||
void* field37_0x94;
|
||||
void* field38_0x98;
|
||||
void (__thiscall *remove)(struct Cell*);
|
||||
void* field40_0xa0;
|
||||
};
|
||||
|
||||
// In the Noita code this would be the ICellBurnable class
|
||||
struct Cell {
|
||||
struct Cell_vtable* vtable;
|
||||
|
||||
int hp;
|
||||
char unknown1[8];
|
||||
bool is_burning;
|
||||
char unknown2[3];
|
||||
uintptr_t material_ptr;
|
||||
};
|
||||
|
||||
struct CLiquidCell {
|
||||
struct Cell cell;
|
||||
int x;
|
||||
int y;
|
||||
char unknown1;
|
||||
char unknown2;
|
||||
bool is_static;
|
||||
char unknown3;
|
||||
int unknown4[3];
|
||||
struct Colour colour;
|
||||
unsigned not_colour;
|
||||
};
|
||||
|
||||
typedef struct Cell (*cell_array)[0x40000];
|
||||
|
||||
struct ChunkMap {
|
||||
int unknown[2];
|
||||
cell_array* (*cells)[0x40000];
|
||||
int unknown2[8];
|
||||
};
|
||||
|
||||
struct GridWorld_vtable {
|
||||
placeholder_memfn* unknown[3];
|
||||
struct ChunkMap* (__thiscall *get_chunk_map)(struct GridWorld* this);
|
||||
placeholder_memfn* unknown2[30];
|
||||
};
|
||||
|
||||
struct GridWorld {
|
||||
struct GridWorld_vtable* vtable;
|
||||
int unknown[318];
|
||||
int world_update_count;
|
||||
struct ChunkMap chunk_map;
|
||||
int unknown2[41];
|
||||
struct GridWorldThreadImpl* mThreadImpl;
|
||||
};
|
||||
|
||||
struct GridWorldThreaded_vtable;
|
||||
|
||||
struct GridWorldThreaded {
|
||||
struct GridWorldThreaded_vtable* vtable;
|
||||
int unknown[287];
|
||||
struct AABB update_region;
|
||||
};
|
||||
|
||||
struct vec_pGridWorldThreaded {
|
||||
struct GridWorldThreaded** begin;
|
||||
struct GridWorldThreaded** end_;
|
||||
struct GridWorldThreaded** capacity_end;
|
||||
};
|
||||
|
||||
struct WorldUpdateParams {
|
||||
struct AABB update_region;
|
||||
int unknown;
|
||||
struct GridWorldThreaded* grid_world_threaded;
|
||||
};
|
||||
|
||||
struct vec_WorldUpdateParams {
|
||||
struct WorldUpdateParams* begin;
|
||||
struct WorldUpdateParams* end_;
|
||||
struct WorldUpdateParams* capacity_end;
|
||||
};
|
||||
|
||||
struct GridWorldThreadImpl {
|
||||
int chunk_update_count;
|
||||
struct vec_pGridWorldThreaded updated_grid_worlds;
|
||||
|
||||
int world_update_params_count;
|
||||
struct vec_WorldUpdateParams world_update_params;
|
||||
|
||||
int grid_with_area_count;
|
||||
struct vec_pGridWorldThreaded with_area_grid_worlds;
|
||||
|
||||
int another_count;
|
||||
int another_vec[3];
|
||||
|
||||
int some_kind_of_ptr;
|
||||
int some_kind_of_counter;
|
||||
|
||||
int last_vec[3];
|
||||
};
|
||||
|
||||
typedef struct Cell** __thiscall get_cell_f(struct ChunkMap*, int x, int y);
|
||||
typedef bool __thiscall chunk_loaded_f(struct ChunkMap*, int x, int y);
|
||||
|
||||
typedef void __thiscall remove_cell_f(struct GridWorld*, void* cell, int x, int y, bool);
|
||||
typedef struct Cell* __thiscall construct_cell_f(struct GridWorld*, int x, int y, void* material_ptr, void* memory);
|
||||
|
||||
]])
|
||||
|
||||
--local function check_celldata_field(f, o)
|
||||
-- local offset = ffi.offsetof("struct CellData", f)
|
||||
-- assert(offset == o, "Expected field " .. f .. " to be at offset " .. o)
|
||||
--end
|
||||
--
|
||||
--check_celldata_field("wang_color", 0x40)
|
||||
--check_celldata_field("generates_flames", 0xa4)
|
||||
--check_celldata_field("durability", 0x104)
|
||||
--check_celldata_field("cold_freezes_to_material", 0x114)
|
||||
--check_celldata_field("liquid_sand", 0x160)
|
||||
--check_celldata_field("liquid_sprite_stain_ignited_drop_chance", 0x17c)
|
||||
--check_celldata_field("gas_horizontal_speed", 0x196)
|
||||
--check_celldata_field("solid_on_sleep_convert", 0x1ad)
|
||||
--check_celldata_field("solid_break_to_type", 0x1d0)
|
||||
--check_celldata_field("vegetation_sprite", 0x20c)
|
||||
--check_celldata_field("wang_noise_type", 0x23c)
|
||||
--check_celldata_field("ignore_self_reaction_warning", 0x269)
|
||||
--check_celldata_field("is_just_particle_fx", 0x289)
|
||||
|
||||
---@class ChunkMap pointer type
|
||||
---@class GridWorld pointer type
|
||||
---@class CellData pointer type
|
||||
---@class Cell pointer type
|
||||
|
||||
---Access a pixel in the world.
|
||||
---You can write a cell created from world_ffi.construct_cell to this pointer to add a cell into the world.
|
||||
---If there's already a cell at this position, make sure to call world_ffi.remove_cell first.
|
||||
---@type fun(chunk_map: ChunkMap, x: integer, y: integer): Cell
|
||||
world_ffi.get_cell = ffi.cast("get_cell_f*", world_info.get_cell)
|
||||
|
||||
---Remove a cell from the world. bool return has unknown meaning.
|
||||
---@type fun(grid_world: GridWorld, cell: Cell, x: integer, y: integer): boolean
|
||||
world_ffi.remove_cell = ffi.cast("remove_cell_f*", world_info.remove_cell)
|
||||
|
||||
---Create a new cell. If memory is null pointer it will allocate its own memory.
|
||||
---@type fun(grid_world: GridWorld, x: integer, y: integer, material: CellData, memory: ffi.cdata*)
|
||||
world_ffi.construct_cell = ffi.cast("construct_cell_f*", world_info.construct_cell)
|
||||
|
||||
---Check if a chunk is loaded. x and y are world coordinates.
|
||||
---```lua
|
||||
---if world_ffi.chunk_loaded(chunk_map, x, y) then
|
||||
--- local cell = world_ffi.get_cell(chunk_map, x, y)
|
||||
--- ..
|
||||
---```
|
||||
---@type fun(chunk_map: ChunkMap, x: integer, y: integer): boolean
|
||||
world_ffi.chunk_loaded = ffi.cast("chunk_loaded_f*", world_info.chunk_loaded)
|
||||
|
||||
world_ffi.Position = ffi.typeof("struct Position")
|
||||
world_ffi.Colour = ffi.typeof("struct Colour")
|
||||
world_ffi.AABB = ffi.typeof("struct AABB")
|
||||
world_ffi.CellType = ffi.typeof("enum CellType")
|
||||
world_ffi.Cell = ffi.typeof("struct Cell")
|
||||
world_ffi.CLiquidCell = ffi.typeof("struct CLiquidCell")
|
||||
world_ffi.ChunkMap = ffi.typeof("struct ChunkMap")
|
||||
world_ffi.GridWorld = ffi.typeof("struct GridWorld")
|
||||
world_ffi.GridWorldThreaded = ffi.typeof("struct GridWorldThreaded")
|
||||
world_ffi.WorldUpdateParams = ffi.typeof("struct WorldUpdateParams")
|
||||
world_ffi.GridWorldThreadImpl = ffi.typeof("struct GridWorldThreadImpl")
|
||||
|
||||
---Get the grid world.
|
||||
---@return GridWorld
|
||||
function world_ffi.get_grid_world()
|
||||
local game_global = ffi.cast("void*", gg_ptr)
|
||||
local world_data = ffi.cast("void**", ffi.cast("char*", game_global) + 0xc)[0]
|
||||
local grid_world = ffi.cast("struct GridWorld**", ffi.cast("char*", world_data) + 0x44)[0]
|
||||
return grid_world
|
||||
end
|
||||
|
||||
local celldata_size = 0x290
|
||||
local CellData_ptr = ffi.typeof("struct CellData*")
|
||||
|
||||
---Turn a standard material id into a material pointer.
|
||||
---@param id integer material id that is used in the standard Noita functions
|
||||
---@return CellData material to internal material data (aka cell data).
|
||||
---```lua
|
||||
---local gold_ptr = world_ffi.get_material_ptr(CellFactory_GetType("gold"))
|
||||
---```
|
||||
function world_ffi.get_material_ptr(id)
|
||||
local game_global = ffi.cast("char*", gg_ptr)
|
||||
local cell_factory = ffi.cast("char**", (game_global + 0x18))[0]
|
||||
local begin = ffi.cast("char**", cell_factory + 0x18)[0]
|
||||
local ptr = begin + celldata_size * id
|
||||
return ffi.cast(CellData_ptr, ptr) --[[@as CellData]]
|
||||
end
|
||||
|
||||
---Turn a material pointer into a standard material id.
|
||||
---@param material CellData to a material (aka cell data)
|
||||
---@return integer material id that is accepted by standard Noita functions such as `CellFactory_GetUIName` and `ConvertMaterialOnAreaInstantly`.
|
||||
---```lua
|
||||
---local mat_id = world_ffi.get_material_id(cell.vtable.get_material(cell))
|
||||
---```
|
||||
---See: `world_ffi.get_material_ptr`
|
||||
function world_ffi.get_material_id(material)
|
||||
local game_global = ffi.cast("char*", gg_ptr)
|
||||
local cell_factory = ffi.cast("char**", (game_global + 0x18))[0]
|
||||
local begin = ffi.cast("char**", cell_factory + 0x18)[0]
|
||||
local offset = ffi.cast("char*", material) - begin
|
||||
return offset / celldata_size
|
||||
end
|
||||
|
||||
return world_ffi
|
||||
|
|
|
@ -911,4 +911,4 @@ function rpc.replicate_projectile(seri_ent, position_x, position_y, target_x, ta
|
|||
GameShootProjectile(source_ent, position_x, position_y, target_x, target_y, ent)
|
||||
end
|
||||
|
||||
return enemy_sync
|
||||
return enemy_sync
|
||||
|
|
|
@ -862,4 +862,4 @@ ctx.cap.item_sync = {
|
|||
|
||||
item_sync.rpc = rpc
|
||||
|
||||
return item_sync
|
||||
return item_sync
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local rpc = net.new_rpc_namespace()
|
||||
local tele = {}
|
||||
local who_has_tele = {}
|
||||
local is_holding
|
||||
|
||||
rpc.opts_reliable()
|
||||
function rpc.end_tele()
|
||||
|
@ -29,6 +30,12 @@ function rpc.send_tele(body_gid, n, extent, aimangle, bodyangle, distance, mindi
|
|||
if not table.contains(who_has_tele, ctx.rpc_peer_id) then
|
||||
table.insert(who_has_tele, ctx.rpc_peer_id)
|
||||
ComponentSetValue2(com, "mState", 1)
|
||||
if is_holding == ent then
|
||||
local mycom = EntityGetFirstComponent(ctx.my_player.entity, "TelekinesisComponent")
|
||||
if mycom ~= nil then
|
||||
ComponentSetValue2(mycom, "mState", 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
ComponentSetValue(com, "mBodyID", body_id)
|
||||
ComponentSetValue2(com, "mStartBodyMaxExtent", extent)
|
||||
|
@ -92,6 +99,7 @@ function tele.on_world_update()
|
|||
end
|
||||
end
|
||||
if gid ~= nil then
|
||||
is_holding = ent
|
||||
has_tele = true
|
||||
rpc.send_tele(
|
||||
ComponentGetValue2(gid, "value_string"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue