Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add get_liquids_at function and small stuff #392

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/game_data/spel2.lua

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/generate_emmylua.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"object": "any",
"ImVec2": "Vec2",
"BucketItem": "any",
"ENTITY_MASK": "MASK",
}

reFloat = re.compile(r"\bfloat\b")
Expand Down
1 change: 1 addition & 0 deletions docs/parse_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"SoundCallbackFunction": "function",
"object ": "any ",
"BucketItem": "any",
"ENTITY_MASK": "MASK",
}

header_files = [
Expand Down
11 changes: 11 additions & 0 deletions docs/src/includes/_globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,17 @@ Gets the value for the specified config

Get the current layer that the liquid is spawn in. Related function [set_liquid_layer](#set_liquid_layer)

### get_liquids_at


> Search script examples for [get_liquids_at](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_liquids_at)

#### int get_liquids_at([MASK](#MASK) liquid_mask, float x, float y, [LAYER](#LAYER) layer)

Optimized function to check for the amount of liquids at a certain position, by accessing a 2d array of liquids by third of a tile. Try the `liquids.lua` example to know better how it works.
Water blobs increase the number by 2 on the grid, while lava blobs increase it by 3. The maximum is usually 6
Coarse water increase the number by 3, coarse and stagnant lava by 6. Combinations of both normal and coarse can make the number higher than 6

### get_local_prng


Expand Down
120 changes: 120 additions & 0 deletions examples/liquids.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
register_option_bool("only_draw_at_mouse", "Testing: Draw only liquid num at mouse position", "", false)
register_option_bool("draw_grid_lines", "Draw grid lines", "", true)
register_option_bool("draw_text_shadow", "Draw text shadow", "", true)

local function get_camera_bounds_grid()
local left, top = game_position(-1, 1)
local right, bottom = game_position(1, -1)
left, top, right, bottom = math.floor(left - 0.5), math.ceil(top + 0.5), math.ceil(right + 0.5), math.floor(bottom - 0.5)
return left, top, right, bottom
end

local water_color = Color:new(0, 0.1, 1.0, 1.0)
local lava_color = Color:new(1.0, 0.1, 0.0, 1.0)
local black = Color:black()

---@type table<integer, TextRenderingInfo>
local num_text_renders = {}
local last_zoom = get_zoom_level()

-- Generate a text for every possible number, to optimize when there's many numbers to render
local function reload_num_text_renders(zoom)
for i = 0, 12 do
num_text_renders[i] = TextRenderingInfo:new(tostring(i), 0.0081 / zoom, 0.0081 / zoom, VANILLA_TEXT_ALIGNMENT.CENTER, VANILLA_FONT_STYLE.NORMAL)
end
end
reload_num_text_renders(last_zoom)

---@param ctx VanillaRenderContext
---@param text_render TextRenderingInfo
local function draw_text_shadow(ctx, text_render)
local offset <const> = 0.027 / last_zoom
local saved_x, saved_y = text_render.x, text_render.y
text_render.x, text_render.y = text_render.x + offset, text_render.y - offset
ctx:draw_text(text_render, black)
text_render.x, text_render.y = saved_x, saved_y
end

---@param ctx VanillaRenderContext
---@param x number
---@param y number
local function draw_liquids_at(ctx, x, y)
local water = get_liquids_at(MASK.WATER, x, y, LAYER.PLAYER)
local lava = get_liquids_at(MASK.LAVA, x, y, LAYER.PLAYER)
if water == 0 and lava == 0 then return end

local spacing <const> = 0.0135 / last_zoom
local text_render = num_text_renders[water]
text_render.x, text_render.y = screen_position(x, y)
text_render.x = text_render.x - (text_render.width / 2.) - spacing
if options.draw_text_shadow then
draw_text_shadow(ctx, text_render)
end
ctx:draw_text(text_render, water_color)

text_render = num_text_renders[lava]
text_render.x, text_render.y = screen_position(x, y)
text_render.x = text_render.x + (text_render.width / 2.) + spacing
if options.draw_text_shadow then
draw_text_shadow(ctx, text_render)
end
ctx:draw_text(text_render, lava_color)
end

---@param ctx VanillaRenderContext
---@param x number
---@param y number
local function draw_grid_liquids(ctx, x, y)
local off <const> = 1./3.
for iy = -1, 1 do
for ix = -1, 1 do
local cx, cy = x + (ix * off), y + (iy * off)
draw_liquids_at(ctx, cx, cy)
end
end
end

local trans_white = Color:white()
trans_white.a = 0.25

---@param ctx VanillaRenderContext
set_callback(function(ctx)
local left, top, right, bottom = get_camera_bounds_grid()
local zoom = get_zoom_level()
if last_zoom ~= zoom then
reload_num_text_renders(zoom)
last_zoom = zoom
end

if not options.only_draw_at_mouse then
for y = bottom, top do
for x = left, right do
draw_grid_liquids(ctx, x, y)
end
end
else
local spacing <const> = 0.0135 / last_zoom
local x, y = game_position(mouse_position())
x = x + (1/6)
y = y + (1/6)
x = x - (x % (1/3))
y = y - (y % (1/3))
draw_liquids_at(ctx, x, y)
end

if not options.draw_grid_lines then return end
local off <const> = 1./3.
local line_width <const> = 3.5
for y = bottom, top do
for i = 0, 2 do
local line_y = y + (off * i) + 0.5
ctx:draw_screen_line(Vec2:new(screen_position(left, line_y)), Vec2:new(screen_position(right, line_y)), line_width, trans_white)
end
end
for x = left, right do
for i = 0, 2 do
local line_x = x + (off * i) + 0.5
ctx:draw_screen_line(Vec2:new(screen_position(line_x, top)), Vec2:new(screen_position(line_x, bottom)), line_width, trans_white)
end
end
end, ON.RENDER_PRE_HUD)
22 changes: 22 additions & 0 deletions src/game_api/aliases.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,25 @@ enum class PAUSE_SCREEN : int64_t
EXIT = 1 << 31,
};
ENUM_CLASS_FLAGS(PAUSE_SCREEN);

enum class ENTITY_MASK
{
PLAYER = 0x1,
MOUNT = 0x2,
MONSTER = 0x4,
ITEM = 0x8,
EXPLOSION = 0x10,
ROPE = 0x20,
FX = 0x40,
ACTIVEFLOOR = 0x80,
FLOOR = 0x100,
DECORATION = 0x200,
BG = 0x400,
SHADOW = 0x800,
LOGICAL = 0x1000,
WATER = 0x2000,
LAVA = 0x4000,
LIQUID = 0x6000,
ANY = 0x0,
};
ENUM_CLASS_FLAGS(ENTITY_MASK)
27 changes: 27 additions & 0 deletions src/game_api/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,33 @@ void add_entity_to_liquid_collision(uint32_t uid, bool add)
}
}

uint8_t get_liquids_at(ENTITY_MASK liquid_mask, float x, float y, LAYER layer)
{
uint8_t actual_layer = enum_to_layer(layer);
LiquidPhysics* liquid_physics = State::get().ptr()->liquid_physics;
if (actual_layer != get_liquid_layer())
return 0;
if (y > 125.5 || y < 0 || x > 85.5 || x < 0)
return 0;

uint32_t ix = static_cast<int>((x + 0.5) / 0.3333333);
uint32_t iy = static_cast<int>((y + 0.5) / 0.3333333);
auto& liquids_at = (*liquid_physics->liquids_by_third_of_tile)[iy][ix];
if (liquid_mask == ENTITY_MASK::ANY)
{
return liquids_at.water + liquids_at.lava;
}
else
{
uint8_t liquids_num = 0;
if ((liquid_mask & ENTITY_MASK::WATER) == ENTITY_MASK::WATER)
liquids_num += liquids_at.water;
if ((liquid_mask & ENTITY_MASK::LAVA) == ENTITY_MASK::LAVA)
liquids_num += liquids_at.lava;
return liquids_num;
}
}

bool disable_floor_embeds(bool disable)
{
static const auto address = get_address("spawn_floor_embeds");
Expand Down
1 change: 1 addition & 0 deletions src/game_api/rpc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ void set_adventure_seed(int64_t first, int64_t second);
std::pair<int64_t, int64_t> get_adventure_seed(std::optional<bool> run_start);
void update_liquid_collision_at(float x, float y, bool add, std::optional<LAYER> layer = std::nullopt);
void add_entity_to_liquid_collision(uint32_t uid, bool add);
uint8_t get_liquids_at(ENTITY_MASK liquid_mask, float x, float y, LAYER layer);
bool disable_floor_embeds(bool disable);
void set_cursepot_ghost_enabled(bool enable);
void game_log(std::string message);
Expand Down
5 changes: 5 additions & 0 deletions src/game_api/script/lua_vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,11 @@ end
/// optional `layer` parameter to be used when liquid was moved to back layer using [set_liquid_layer](#set_liquid_layer)
lua["update_liquid_collision_at"] = update_liquid_collision_at;

/// Optimized function to check for the amount of liquids at a certain position, by accessing a 2d array of liquids by third of a tile. Try the `liquids.lua` example to know better how it works.
/// Water blobs increase the number by 2 on the grid, while lava blobs increase it by 3. The maximum is usually 6
/// Coarse water increase the number by 3, coarse and stagnant lava by 6. Combinations of both normal and coarse can make the number higher than 6
lua["get_liquids_at"] = get_liquids_at;

/// Disable all crust item spawns, returns whether they were already disabled before the call
lua["disable_floor_embeds"] = disable_floor_embeds;

Expand Down
24 changes: 16 additions & 8 deletions src/game_api/state_structs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,14 @@ struct LiquidLake
Entity* impostor_lake;
};

// Water blobs increase the number by 2 on the grid, while lava blobs increase it by 3. The maximum is 6
// Coarse water increase the number by 3, coarse and stagnant lava by 6. Combinations of both normal and coarse can make the number higher than 6
struct LiquidAmounts
{
uint8_t lava;
uint8_t water;
};

struct LiquidPhysics
{
size_t unknown1; // MysteryLiquidPointer1 in plugin, collision with floors/activefloors related
Expand All @@ -867,16 +875,16 @@ struct LiquidPhysics
LiquidTileSpawnData stagnant_lava_tile_spawn_data;
};
};
custom_map<std::pair<uint8_t, uint8_t>, size_t*>* floors; // key is a grid position, the struct seams to be the same as in push_blocks
custom_map<uint32_t, size_t*>* push_blocks; // key is uid, not sure about the struct it points to (it's also possible that the value is 2 pointers)
custom_vector<LiquidLake> impostor_lakes; //
uint32_t total_liquid_spawned; // Total number of spawned liquid entities, all types.
uint32_t unknown8; // padding probably
uint8_t* unknown9; // array byte* ? game allocates 0x2F9E8 bytes for it, (0x2F9E8 / g_level_max_x * g_level_max_y = 18) which is weird, but i still think it's position based index, maybe it's 16 and accounts for more rows (grater level height)
// always allocates after the LiquidPhysics
custom_map<std::pair<uint8_t, uint8_t>, size_t*>* floors; // key is a grid position, the struct seams to be the same as in push_blocks
custom_map<uint32_t, size_t*>* push_blocks; // key is uid, not sure about the struct it points to (it's also possible that the value is 2 pointers)
custom_vector<LiquidLake> impostor_lakes; //
uint32_t total_liquid_spawned; // Total number of spawned liquid entities, all types.
uint32_t unknown8; // padding probably
LiquidAmounts (*liquids_by_third_of_tile)[126 * 3][86 * 3]; // array byte* game allocates 0x2F9E8 bytes for it ((126 * 3) * (86 * 3) * 2 : y, x, liquid_type).
// always allocates after the LiquidPhysics

uint32_t total_liquid_spawned2; // Same as total_liquid_spawned?
bool unknown12;
bool unknown12; // if false, I think the game should check for liquids by looking for liquid entities rather than using the previous liquids array. Is set to true by the game actively
uint8_t padding12a;
uint8_t padding12b;
uint8_t padding12c;
Expand Down
Loading