diff --git a/docs/examples/set_camera_layer_control_enabled.lua b/docs/examples/set_camera_layer_control_enabled.lua new file mode 100644 index 000000000..633d07ac4 --- /dev/null +++ b/docs/examples/set_camera_layer_control_enabled.lua @@ -0,0 +1,23 @@ +set_camera_layer_control_enabled(false) + +g_current_timer = nil +-- default load_time 36 +function change_layer(layer_to, load_time) + + if state.camera_layer == layer_to then + return + end + if g_current_timer ~= nil then + clear_callback(g_current_timer) + g_current_timer = nil + end + -- if we don't want the load time, we can just change the actual layer + if load_time == nil or load_time == 0 then + state.camera_layer = layer_to + return + end + + state.layer_transition_timer = load_time + state.transition_to_layer = layer_to + state.camera_layer = layer_to +end diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index e25d07c15..63eef3c97 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -1060,20 +1060,30 @@ function add_item_to_shop(item_uid, shop_owner_uid) end ---@param frames integer ---@return nil function change_poison_timer(frames) end ----Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example +---Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. +---@param pos Vec2 +---@param color Color +---@param type LIGHT_TYPE +---@param size number +---@param flags integer +---@param uid integer +---@param layer LAYER +---@return Illumination +function create_illumination(pos, color, type, size, flags, uid, layer) end +---Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. ---@param color Color ---@param size number ---@param x number ---@param y number ---@return Illumination function create_illumination(color, size, x, y) end ----Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example +---Creates a new Illumination. Don't forget to continuously call [refresh_illumination](https://spelunky-fyi.github.io/overlunky/#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. ---@param color Color ---@param size number ---@param uid integer ---@return Illumination function create_illumination(color, size, uid) end ----Refreshes an Illumination, keeps it from fading out +---Refreshes an Illumination, keeps it from fading out (updates the timer, keeping it in sync with the game render) ---@param illumination Illumination ---@return nil function refresh_illumination(illumination) end @@ -1310,6 +1320,12 @@ function buttons_to_inputs(x, y, buttons) end ---@param enable boolean ---@return nil function set_infinite_loop_detection_enabled(enable) end +---This disables the `state.camera_layer` to be forced to the `(leader player).layer` and setting of the `state.layer_transition_timer` & `state.transition_to_layer` when player enters layer door. +---Letting you control those manually. +---Look at the example on how to mimic game layer switching behavior +---@param enable boolean +---@return nil +function set_camera_layer_control_enabled(enable) end ---@return boolean function toast_visible() end ---@return boolean @@ -1355,16 +1371,16 @@ function set_character_heart_color(type_id, color) end ---@return CustomMovableBehavior function make_custom_behavior(behavior_name, state_id, base_behavior) end ---Get the [ParticleDB](https://spelunky-fyi.github.io/overlunky/#ParticleDB) details of the specified ID ----@param id integer +---@param id PARTICLEEMITTER ---@return ParticleDB function get_particle_type(id) end ---Generate particles of the specified type around the specified entity uid (use e.g. `local emitter = generate_world_particles(PARTICLEEMITTER.PETTING_PET, players[1].uid)`). You can then decouple the emitter from the entity with `emitter.entity_uid = -1` and freely move it around. See the `particles.lua` example script for more details. ----@param particle_emitter_id integer +---@param particle_emitter_id PARTICLEEMITTER ---@param uid integer ---@return ParticleEmitterInfo function generate_world_particles(particle_emitter_id, uid) end ---Generate particles of the specified type at a certain screen coordinate (use e.g. `local emitter = generate_screen_particles(PARTICLEEMITTER.CHARSELECTOR_TORCHFLAME_FLAMES, 0.0, 0.0)`). See the `particles.lua` example script for more details. ----@param particle_emitter_id integer +---@param particle_emitter_id PARTICLEEMITTER ---@param x number ---@param y number ---@return ParticleEmitterInfo @@ -1976,12 +1992,12 @@ do ---@field pools LiquidPool[] @size: 5 ---@class StateMemory - ---@field screen_last integer @Previous SCREEN, used to check where we're coming from when loading another SCREEN - ---@field screen integer @Current SCREEN, generally read-only or weird things will happen - ---@field screen_next integer @Next SCREEN, used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. + ---@field screen_last SCREEN @Previous SCREEN, used to check where we're coming from when loading another SCREEN + ---@field screen SCREEN @Current SCREEN, generally read-only or weird things will happen + ---@field screen_next SCREEN @Next SCREEN, used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. ---@field ingame integer @Is 1 when you in a game, is set to 0 or 1 in main menu, can't be trusted there, normally in a level is 1 unless you go to the options ---@field playing integer @Is 1 when you are in a level, but going to options sets it to 0 and does not set it back to 1 after the way back, don't trust it - ---@field pause PAUSE @8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controller by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. + ---@field pause PAUSE @8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controlled by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. ---@field width integer @level width in rooms (number of rooms horizontally) ---@field height integer @level height in rooms (number of rooms vertically) ---@field kali_favor integer @@ -1998,7 +2014,7 @@ do ---@field level_start integer @Level number to start new runs in ---@field theme THEME @Current THEME number, used to pick the music and by some game logic like choosing the next level on transition ---@field theme_next THEME @Next THEME number, used when loading a new level or transition - ---@field theme_start integer @THEME to start new runs in + ---@field theme_start THEME @THEME to start new runs in ---@field current_theme ThemeInfo @Points to the current ThemeInfo ---@field force_current_theme fun(self, t: integer): nil @This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! ---@field shoppie_aggro integer @Current shoppie aggro @@ -2025,7 +2041,7 @@ do ---@field win_state integer @0 = no win 1 = tiamat win 2 = hundun win 3 = CO win; set this and next doorway leads to victory scene ---@field illumination Illumination @The global level illumination, very big and bright. ---@field money_last_levels integer - ---@field money_shop_total integer @Total negative amount spent in shops during the run

The total money currently available (in single player) is `players[1].inventory.money + players[1].inventory.collected_money_total + state.money_shop_total` + ---@field money_shop_total integer @Total amount spent in shops and sold idols during the run

The total money currently available is `loop (players[].inventory.money + players[].inventory.collected_money_total) + state.money_shop_total` ---@field player_inputs PlayerInputs @Access the player inputs even when no player entities are available ---@field quests QuestsInfo @NPC quest states ---@field camera Camera @Camera bounds and position @@ -2038,8 +2054,9 @@ do ---@field level_gen LevelGenSystem @Entrance and exit coordinates, shop types and all themes ---@field correct_ushabti integer @See `get_correct_ushabti`. == anim_frame - (2 * floor(anim_frame/12)) ---@field items Items @Has the current player count, player inventories and character selections - ---@field camera_layer integer @The currently drawn layer, can't be changed + ---@field camera_layer integer ---@field layer_transition_timer integer + ---@field transition_to_layer integer ---@field screen_team_select ScreenTeamSelect ---@field screen_character_select ScreenCharacterSelect ---@field screen_transition ScreenTransition @@ -2079,7 +2096,7 @@ do ---@field journal_progress_stain_count integer ---@field journal_progress_stain_slots JournalProgressStainSlot[] @size: 30 @blood splats and paw prints in journal progress page ---@field journal_progress_theme_count integer - ---@field journal_progress_theme_slots integer[] @size: 9 @visited themes in journal progress page + ---@field journal_progress_theme_slots THEME[] @size: 9 @visited themes in journal progress page ---@field theme_info ThemeInfo @Points to the current ThemeInfo ---@field logic LogicList @Level logic like dice game and cutscenes ---@field liquid LiquidPhysics @@ -2091,6 +2108,7 @@ do ---@field green number ---@field blue number ---@field size number + ---@field as_color fun(self): Color @Returns LightParams as Color, note that size = alpha ---@class Illumination ---@field lights LightParams[] @size: 4 @Table of light1, light2, ... etc. @@ -2106,8 +2124,9 @@ do ---@field offset_y number ---@field distortion number ---@field entity_uid integer + ---@field timer integer ---@field flags integer @see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags - ---@field type_flags integer @Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags + ---@field type_flags LIGHT_TYPE ---@field enabled boolean ---@field layer integer @@ -2248,6 +2267,7 @@ function PRNG:random(min, max) end ---@field set_rgba fun(self, red: integer, green: integer, blue: integer, alpha: integer): Color @Changes color based on given RGBA colors in 0..255 range ---@field get_ucolor fun(self): uColor @Returns the `uColor` used in `GuiDrawContext` drawing functions ---@field set_ucolor fun(self, color: uColor): Color @Changes color based on given uColor + ---@field set fun(self, other: Color): Color @Copies the values of different Color to this one ---@class Animation ---@field id integer @@ -3590,7 +3610,7 @@ function Movable:generic_update_world(move, sprint_factor, disable_gravity, on_r ---@field emitted_light Illumination ---@class FlameSize : Flame - ---@field flame_size number @if changed, gradually goes down (0.03 per frame) to the default size + ---@field flame_size number @if changed, gradually goes down (0.03 per frame) to the default size, it's the base value for `entity.width` and `entity.height` ---@class ClimbableRope : Movable ---@field segment_nr_inverse integer @@ -4347,7 +4367,7 @@ function MovableBehavior:get_state_id() end ---@field set_get_next_state_id fun(self, get_next_state_id: fun(movable: Movable, base_fun: function): integer): nil @Set the `get_next_state_id` function of a `CustomMovableBehavior`, this will be called every frame when
the movable is updated. If an `get_next_state_id` is already set it will be overridden. The signature
of the function is `int get_next_state_id(Movable movable, function base_fun))`, use this to move to another state, return `nil`.
or this behaviors `state_id` to remain in this behavior. If no base behavior is set `base_fun` will be `nil`. ---@class ParticleDB - ---@field id integer + ---@field id PARTICLEEMITTER ---@field spawn_count_min integer ---@field spawn_count integer ---@field lifespan_min integer @@ -4380,6 +4400,7 @@ function MovableBehavior:get_state_id() end ---@class ParticleEmitterInfo ---@field particle_type ParticleDB + ---@field particle_type2 ParticleDB ---@field particle_count integer ---@field particle_count_back_layer integer ---@field entity_uid integer @@ -4387,6 +4408,8 @@ function MovableBehavior:get_state_id() end ---@field y number ---@field offset_x number ---@field offset_y number + ---@field layer integer + ---@field draw_depth integer ---@field emitted_particles Particle[] ---@field emitted_particles_back_layer Particle[] @@ -5328,6 +5351,7 @@ function VanillaRenderContext:draw_world_poly_filled(points, color) end ---@field y number ---@field rotate fun(self, angle: number, px: number, py: number): Vec2 ---@field distance_to fun(self, other: Vec2): number @Just simple pythagoras theorem + ---@field set fun(self, other: Vec2): Vec2 ---@field split fun(self): number, number ---@class AABB @@ -5342,6 +5366,7 @@ function VanillaRenderContext:draw_world_poly_filled(points, color) end ---@field center fun(self): number, number @Short for `(aabb.left + aabb.right) / 2.0f, (aabb.top + aabb.bottom) / 2.0f`. ---@field width fun(self): number @Short for `aabb.right - aabb.left`. ---@field height fun(self): number @Short for `aabb.top - aabb.bottom`. + ---@field set fun(self, other: AABB): AABB ---@field split fun(self): number, number, number, number local AABB = nil ---Grows or shrinks the AABB by the given amount in all directions. @@ -5373,6 +5398,7 @@ function AABB:is_point_inside(x, y) end ---@field get_angles fun(self): number, number, number @Returns ABC, BCA, CAB angles in radians ---@field scale fun(self, scale: number): Triangle ---@field area fun(self): number + ---@field set fun(self, other: Triangle): Triangle ---@field split fun(self): Vec2, Vec2, Vec2 @Returns the corner points local Triangle = nil ---@param off Vec2 @@ -5408,6 +5434,7 @@ function Triangle:is_point_inside(x, y, epsilon) end ---@field rotate fun(self, angle: number, px: number, py: number): Quad @Rotates a Quad by an angle, px/py are not offsets, use `:get_AABB():center()` to get approximated center for simetrical quadrangle ---@field flip_horizontally fun(self): Quad ---@field flip_vertically fun(self): Quad + ---@field set fun(self, other: Quad): Quad ---@field split fun(self): Vec2, Vec2, Vec2, Vec2 @Returns the corners in order: bottom_left, bottom_right, top_right, top_left local Quad = nil ---Check if point lies inside of triangle @@ -5643,6 +5670,7 @@ function Quad:is_point_inside(x, y, epsilon) end ---@class ScreenLevel : Screen ---@field buttons integer + ---@field time_till_death_screen integer @Delay after player death to open the death screen ---@class ScreenTransition : Screen ---@field woodpanel_pos number @@ -5812,7 +5840,7 @@ function Quad:is_point_inside(x, y, epsilon) end ---@field page_timer integer ---@field fade_timer integer ---@field opacity integer - ---@field pages JournalPage[] @Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_POST_DRAW_DEPTH](#ON-RENDER_PRE_JOURNAL_PAGE)) + ---@field pages JournalPage[] @Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_PRE_JOURNAL_PAGE](#ON-RENDER_PRE_JOURNAL_PAGE)) ---@class JournalPage ---@field background TextureRenderingInfo @@ -6318,6 +6346,14 @@ function EntityDB:new(other) end ---@return EntityDB function EntityDB:new(other) end +ParticleDB = nil +---@param other ParticleDB +---@return ParticleDB +function ParticleDB:new(other) end +---@param particle_id PARTICLEEMITTER +---@return ParticleDB +function ParticleDB:new(particle_id) end + CustomTheme = nil ---Create a new theme with an id and base theme, overriding defaults. Check [theme functions that are default enabled here](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/script/usertypes/level_lua.cpp). ---@param theme_id_ integer diff --git a/docs/generate.py b/docs/generate.py index 7890dabb5..ac37baa95 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -400,7 +400,7 @@ def print_lf(lf): ---- | ---- | -----------""" ) print("bool | all:empty() | Returns true if container is empty, false otherwise") -print("int | aLL:size() | Same as `#container`") +print("int | all:size() | Same as `#container`") print("any | vector:at(int index) | Same as `vector[index]`") print("any | span:at(int index) | Same as `span[index]`") diff --git a/docs/parse_source.py b/docs/parse_source.py index 7d58ff398..b1d8b4336 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -27,6 +27,7 @@ "../src/game_api/game_manager.hpp", "../src/game_api/state.hpp", "../src/game_api/state_structs.hpp", + "../src/game_api/illumination.hpp", "../src/game_api/prng.hpp", "../src/game_api/entities_floors.hpp", "../src/game_api/entities_activefloors.hpp", diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md index 78e066c82..378ca43f0 100644 --- a/docs/src/includes/_enums.md +++ b/docs/src/includes/_enums.md @@ -654,6 +654,20 @@ Name | Data | Description [MAX_LIQUID_PARTICLES](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LEVEL_CONFIG.MAX_LIQUID_PARTICLES) | 15 | [FLAGGED_LIQUID_ROOMS](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LEVEL_CONFIG.FLAGGED_LIQUID_ROOMS) | 16 | +## LIGHT_TYPE + + +> Search script examples for [LIGHT_TYPE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE) + + + +Name | Data | Description +---- | ---- | ----------- +[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.NONE) | LIGHT_TYPE::NONE | Normal static light, position can be edited to move it around
+[FOLLOW_CAMERA](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.FOLLOW_CAMERA) | LIGHT_TYPE::FOLLOW_CAMERA | Position is updated to the camera position, can be moved around via offset
+[FOLLOW_ENTITY](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.FOLLOW_ENTITY) | LIGHT_TYPE::FOLLOW_ENTITY | Position is updated to the entity position (from the uid field), if the uid is not found it will behave as [LIGHT_TYPE](#LIGHT_TYPE).NONE, can be moved around via offset
+[ROOM_LIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=LIGHT_TYPE.ROOM_LIGHT) | LIGHT_TYPE::ROOM_LIGHT | Rectangle, full brightness always uses light1, disabling light1 does nothing
+ ## LIQUID_POOL diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index 9543ea915..fb4b69e9b 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -158,7 +158,7 @@ They come with some extra functionality: Type | Name | Description ---- | ---- | ----------- bool | all:empty() | Returns true if container is empty, false otherwise -int | aLL:size() | Same as `#container` +int | all:size() | Same as `#container` any | vector:at(int index) | Same as `vector[index]` any | span:at(int index) | Same as `span[index]` any | set:at(int order) | Returns elements in order, it's not an index as sets don't have one @@ -1710,6 +1710,45 @@ Seed the game prng. Set the current adventure seed pair +### set_camera_layer_control_enabled + + +```lua +set_camera_layer_control_enabled(false) + +g_current_timer = nil +-- default load_time 36 +function change_layer(layer_to, load_time) + + if state.camera_layer == layer_to then + return + end + if g_current_timer ~= nil then + clear_callback(g_current_timer) + g_current_timer = nil + end + -- if we don't want the load time, we can just change the actual layer + if load_time == nil or load_time == 0 then + state.camera_layer = layer_to + return + end + + state.layer_transition_timer = load_time + state.transition_to_layer = layer_to + state.camera_layer = layer_to +end + +``` + + +> Search script examples for [set_camera_layer_control_enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_camera_layer_control_enabled) + +#### nil set_camera_layer_control_enabled(bool enable) + +This disables the `state.camera_layer` to be forced to the `(leader player).layer` and setting of the `state.layer_transition_timer` & `state.transition_to_layer` when player enters layer door. +Letting you control those manually. +Look at the example on how to mimic game layer switching behavior + ### set_character_heart_color @@ -2042,11 +2081,13 @@ Steal input from a [Player](#Player), HiredHand or [PlayerGhost](#PlayerGhost) > Search script examples for [create_illumination](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=create_illumination) +#### [Illumination](#Illumination) create_illumination([Vec2](#Vec2) pos, [Color](#Color) color, [LIGHT_TYPE](#LIGHT_TYPE) type, float size, int flags, int uid, [LAYER](#LAYER) layer) + #### [Illumination](#Illumination) create_illumination([Color](#Color) color, float size, float x, float y) #### [Illumination](#Illumination) create_illumination([Color](#Color) color, float size, int uid) -Creates a new [Illumination](#Illumination). Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example +Creates a new [Illumination](#Illumination). Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. ### refresh_illumination @@ -2055,7 +2096,7 @@ Creates a new [Illumination](#Illumination). Don't forget to continuously call [ #### nil refresh_illumination([Illumination](#Illumination) illumination) -Refreshes an [Illumination](#Illumination), keeps it from fading out +Refreshes an [Illumination](#Illumination), keeps it from fading out (updates the timer, keeping it in sync with the game render) ## Message functions @@ -2376,7 +2417,7 @@ Extinguish a particle emitter (use the return value of `generate_world_particles > Search script examples for [generate_screen_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=generate_screen_particles) -#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_screen_particles(int particle_emitter_id, float x, float y) +#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_screen_particles([PARTICLEEMITTER](#PARTICLEEMITTER) particle_emitter_id, float x, float y) Generate particles of the specified type at a certain screen coordinate (use e.g. `local emitter = generate_screen_particles(PARTICLEEMITTER.CHARSELECTOR_TORCHFLAME_FLAMES, 0.0, 0.0)`). See the `particles.lua` example script for more details. @@ -2385,7 +2426,7 @@ Generate particles of the specified type at a certain screen coordinate (use e.g > Search script examples for [generate_world_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=generate_world_particles) -#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_world_particles(int particle_emitter_id, int uid) +#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_world_particles([PARTICLEEMITTER](#PARTICLEEMITTER) particle_emitter_id, int uid) Generate particles of the specified type around the specified entity uid (use e.g. `local emitter = generate_world_particles(PARTICLEEMITTER.PETTING_PET, players[1].uid)`). You can then decouple the emitter from the entity with `emitter.entity_uid = -1` and freely move it around. See the `particles.lua` example script for more details. @@ -2394,7 +2435,7 @@ Generate particles of the specified type around the specified entity uid (use e. > Search script examples for [get_particle_type](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_particle_type) -#### [ParticleDB](#ParticleDB) get_particle_type(int id) +#### [ParticleDB](#ParticleDB) get_particle_type([PARTICLEEMITTER](#PARTICLEEMITTER) id) Get the [ParticleDB](#ParticleDB) details of the specified ID @@ -3634,7 +3675,7 @@ Use `get_entities_overlapping_hitbox` instead #### int get_entity_ai_state(int uid) -As the name is misleading. use entity `move_state` field instead +As the name is misleading. use [Movable](#Movable).`move_state` field instead ### set_arrowtrap_projectile @@ -3905,7 +3946,7 @@ Use this only when no other approach works, this call can be expensive if overus > Search script examples for [generate_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=generate_particles) -#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_particles(int particle_emitter_id, int uid) +#### [ParticleEmitterInfo](#ParticleEmitterInfo) generate_particles([PARTICLEEMITTER](#PARTICLEEMITTER) particle_emitter_id, int uid) Use `generate_world_particles` diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index e93f99fbf..40963f3e2 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -571,6 +571,7 @@ float | [width()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=width float | [height()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=height) | Short for `aabb.top - aabb.bottom`. bool | [is_point_inside(Vec2 p)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | Checks if point lies between left/right and top/bottom bool | [is_point_inside(float x, float y)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | +[AABB](#AABB) | [set(AABB other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<float, float, float, float> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | ### BackgroundMusic @@ -640,6 +641,7 @@ tuple<int, int, int, int> | [get_rgba()](https://github.com/spelunky-fyi/o [Color](#Color) | [set_rgba(int red, int green, int blue, int alpha)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_rgba) | Changes color based on given RGBA colors in 0..255 range [uColor](#Aliases) | [get_ucolor()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_ucolor) | Returns the `uColor` used in `GuiDrawContext` drawing functions [Color](#Color) | [set_ucolor(uColor color)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_ucolor) | Changes color based on given [uColor](#Aliases) +[Color](#Color) | [set(Color other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | Copies the values of different [Color](#Color) to this one ### CutsceneBehavior @@ -809,6 +811,7 @@ float | [top_left_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=to [Quad](#Quad) | [flip_vertically()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flip_vertically) | bool | [is_point_inside(Vec2 p, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | Check if point lies inside of triangle
Because of the imprecise nature of floating point values, the `epsilon` value is needed to compare the floats, the default value is `0.00001` bool | [is_point_inside(float x, float y, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | +[Quad](#Quad) | [set(Quad other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<[Vec2](#Vec2), [Vec2](#Vec2), [Vec2](#Vec2), [Vec2](#Vec2)> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | Returns the corners in order: bottom_left, bottom_right, top_right, top_left ### RenderInfo @@ -924,6 +927,7 @@ tuple<float, float, float> | [get_angles()](https://github.com/spelunky-fy float | [area()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=area) | bool | [is_point_inside(Vec2 p, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | Check if point lies inside of triangle
Because of the imprecise nature of floating point values, the `epsilon` value is needed to compare the floats, the default value is `0.0001` bool | [is_point_inside(float x, float y, optional epsilon)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=is_point_inside) | +[Triangle](#Triangle) | [set(Triangle other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<[Vec2](#Vec2), [Vec2](#Vec2), [Vec2](#Vec2)> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | Returns the corner points ### UdpServer @@ -945,6 +949,7 @@ float | [x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=x) | float | [y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=y) | [Vec2](#Vec2) | [rotate(float angle, float px, float py)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=rotate) | float | [distance_to(Vec2 other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=distance_to) | Just simple pythagoras theorem +[Vec2](#Vec2) | [set(Vec2 other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set) | tuple<float, float> | [split()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=split) | ## Input types @@ -1287,8 +1292,9 @@ float | [offset_x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offs float | [offset_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offset_y) | float | [distortion](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=distortion) | int | [entity_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=entity_uid) | +int | [timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=timer) | int | [flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flags) | see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags -int | [type_flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=type_flags) | Only one can be set: 1 - Follow camera, 2 - Follow [Entity](#Entity), 3 - Rectangle, full brightness
Rectangle always uses light1, even when it's disabled in flags +[LIGHT_TYPE](#LIGHT_TYPE) | [type_flags](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=type_flags) | bool | [enabled](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=enabled) | int | [layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer) | @@ -1302,6 +1308,7 @@ float | [red](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=red) | float | [green](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=green) | float | [blue](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=blue) | float | [size](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=size) | +[Color](#Color) | [as_color()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=as_color) | Returns [LightParams](#LightParams) as [Color](#Color), note that size = alpha ## Liquid types @@ -1658,7 +1665,9 @@ Used in [ParticleDB](#ParticleDB), [get_particle_type](#get_particle_type) Type | Name | Description ---- | ---- | ----------- -int | [id](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=id) | +[ParticleDB](#ParticleDB) | [new(ParticleDB other)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ParticleDB) | +[ParticleDB](#ParticleDB) | [new(PARTICLEEMITTER particle_id)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ParticleDB) | +[PARTICLEEMITTER](#PARTICLEEMITTER) | [id](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=id) | int | [spawn_count_min](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn_count_min) | int | [spawn_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=spawn_count) | int | [lifespan_min](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=lifespan_min) | @@ -1697,6 +1706,7 @@ Used in [ScreenCharacterSelect](#ScreenCharacterSelect), [ScreenTitle](#ScreenTi Type | Name | Description ---- | ---- | ----------- [ParticleDB](#ParticleDB) | [particle_type](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_type) | +[ParticleDB](#ParticleDB) | [particle_type2](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_type2) | int | [particle_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_count) | int | [particle_count_back_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=particle_count_back_layer) | int | [entity_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=entity_uid) | @@ -1704,6 +1714,8 @@ float | [x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=x) | float | [y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=y) | float | [offset_x](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offset_x) | float | [offset_y](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=offset_y) | +int | [layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer) | +int | [draw_depth](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=draw_depth) | array<[Particle](#Particle)> | [emitted_particles](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=emitted_particles) | array<[Particle](#Particle)> | [emitted_particles_back_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=emitted_particles_back_layer) | @@ -1846,7 +1858,7 @@ int | [max_page_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q= int | [page_timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=page_timer) | int | [fade_timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=fade_timer) | int | [opacity](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=opacity) | -vector<[JournalPage](#JournalPage)> | [pages](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pages) | Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_POST_DRAW_DEPTH](#ON-RENDER_PRE_JOURNAL_PAGE)) +vector<[JournalPage](#JournalPage)> | [pages](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pages) | Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page.
Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_PRE_JOURNAL_PAGE](#ON-RENDER_PRE_JOURNAL_PAGE)) ### OnlineLobbyScreenPlayer @@ -2261,6 +2273,7 @@ Derived from [Screen](#Screen) Type | Name | Description ---- | ---- | ----------- int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | +int | [time_till_death_screen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=time_till_death_screen) | Delay after player death to open the death screen ### ScreenLogo @@ -2817,12 +2830,12 @@ Can be accessed via global [state](#state) Type | Name | Description ---- | ---- | ----------- -int | [screen_last](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_last) | Previous [SCREEN](#SCREEN), used to check where we're coming from when loading another [SCREEN](#SCREEN) -int | [screen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen) | Current [SCREEN](#SCREEN), generally read-only or weird things will happen -int | [screen_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_next) | Next [SCREEN](#SCREEN), used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. +[SCREEN](#SCREEN) | [screen_last](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_last) | Previous [SCREEN](#SCREEN), used to check where we're coming from when loading another [SCREEN](#SCREEN) +[SCREEN](#SCREEN) | [screen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen) | Current [SCREEN](#SCREEN), generally read-only or weird things will happen +[SCREEN](#SCREEN) | [screen_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_next) | Next [SCREEN](#SCREEN), used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. int | [ingame](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ingame) | Is 1 when you in a game, is set to 0 or 1 in main menu, can't be trusted there, normally in a level is 1 unless you go to the options int | [playing](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=playing) | Is 1 when you are in a level, but going to options sets it to 0 and does not set it back to 1 after the way back, don't trust it -[PAUSE](#PAUSE) | [pause](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause) | 8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controller by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. +[PAUSE](#PAUSE) | [pause](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause) | 8bit flags, multiple might be active at the same time
1: Menu: Pauses the level timer and engine. Can't set, controlled by the menu.
2: Fade/Loading: Pauses all timers and engine.
4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes.
8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
16: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs.
32: Ankh: Pauses all timers, engine, but not camera. Used by the ankh cutscene. int | [width](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=width) | level width in rooms (number of rooms horizontally) int | [height](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=height) | level height in rooms (number of rooms vertically) int | [kali_favor](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=kali_favor) | @@ -2839,7 +2852,7 @@ int | [level_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=leve int | [level_start](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=level_start) | Level number to start new runs in [THEME](#THEME) | [theme](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme) | Current [THEME](#THEME) number, used to pick the music and by some game logic like choosing the next level on transition [THEME](#THEME) | [theme_next](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_next) | Next [THEME](#THEME) number, used when loading a new level or transition -int | [theme_start](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_start) | [THEME](#THEME) to start new runs in +[THEME](#THEME) | [theme_start](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_start) | [THEME](#THEME) to start new runs in [ThemeInfo](#ThemeInfo) | [current_theme](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=current_theme) | Points to the current [ThemeInfo](#ThemeInfo) nil | [force_current_theme(int t)](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=force_current_theme) | This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! int | [shoppie_aggro](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=shoppie_aggro) | Current shoppie aggro @@ -2866,7 +2879,7 @@ int | [saved_hamsters](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q= int | [win_state](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=win_state) | 0 = no win 1 = tiamat win 2 = hundun win 3 = CO win; set this and next doorway leads to victory scene [Illumination](#Illumination) | [illumination](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=illumination) | The global level illumination, very big and bright. int | [money_last_levels](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=money_last_levels) | -int | [money_shop_total](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=money_shop_total) | Total negative amount spent in shops during the run

The total money currently available (in single player) is `players[1].inventory.money + players[1].inventory.collected_money_total + state.money_shop_total` +int | [money_shop_total](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=money_shop_total) | Total amount spent in shops and sold idols during the run

The total money currently available is `loop (players[].inventory.money + players[].inventory.collected_money_total) + state.money_shop_total` [PlayerInputs](#PlayerInputs) | [player_inputs](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=player_inputs) | Access the player inputs even when no player entities are available [QuestsInfo](#QuestsInfo) | [quests](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=quests) | [NPC](#NPC) quest states [Camera](#Camera) | [camera](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=camera) | [Camera](#Camera) bounds and position @@ -2879,8 +2892,9 @@ int | [speechbubble_owner](https://github.com/spelunky-fyi/overlunky/search?l=Lu [LevelGenSystem](#LevelGenSystem) | [level_gen](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=level_gen) | Entrance and exit coordinates, shop types and all themes int | [correct_ushabti](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=correct_ushabti) | See `get_correct_ushabti`. == anim_frame - (2 * floor(anim_frame/12)) [Items](#Items) | [items](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=items) | Has the current player count, player inventories and character selections -int | [camera_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=camera_layer) | The currently drawn layer, can't be changed +int | [camera_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=camera_layer) | int | [layer_transition_timer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=layer_transition_timer) | +int | [transition_to_layer](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=transition_to_layer) | [ScreenTeamSelect](#ScreenTeamSelect) | [screen_team_select](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_team_select) | [ScreenCharacterSelect](#ScreenCharacterSelect) | [screen_character_select](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_character_select) | [ScreenTransition](#ScreenTransition) | [screen_transition](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=screen_transition) | @@ -2920,7 +2934,7 @@ array<[JournalProgressStickerSlot](#JournalProgressStickerSlot), 40> | [jo int | [journal_progress_stain_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_stain_count) | array<[JournalProgressStainSlot](#JournalProgressStainSlot), 30> | [journal_progress_stain_slots](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_stain_slots) | blood splats and paw prints in journal progress page int | [journal_progress_theme_count](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_theme_count) | -array<int, 9> | [journal_progress_theme_slots](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_theme_slots) | visited themes in journal progress page +array<[THEME](#THEME), 9> | [journal_progress_theme_slots](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_progress_theme_slots) | visited themes in journal progress page [ThemeInfo](#ThemeInfo) | [theme_info](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=theme_info) | Points to the current [ThemeInfo](#ThemeInfo) [LogicList](#LogicList) | [logic](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=logic) | Level logic like dice game and cutscenes [LiquidPhysics](#LiquidPhysics) | [liquid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=liquid) | @@ -6032,7 +6046,7 @@ Derived from [Entity](#Entity) [Movable](#Movable) [Flame](#Flame) Type | Name | Description ---- | ---- | ----------- -float | [flame_size](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flame_size) | if changed, gradually goes down (0.03 per frame) to the default size +float | [flame_size](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=flame_size) | if changed, gradually goes down (0.03 per frame) to the default size, it's the base value for `entity.width` and `entity.height` ### Fly diff --git a/src/game_api/color.hpp b/src/game_api/color.hpp index 5cb196d83..fe9288e97 100644 --- a/src/game_api/color.hpp +++ b/src/game_api/color.hpp @@ -153,6 +153,12 @@ struct Color uint8_t alpha = (color >> 24U) & 0xFF; return set_rgba(red, green, blue, alpha); } + /// Copies the values of different Color to this one + Color& set(Color& other) + { + *this = other; + return *this; + } float r{0.0f}; float g{0.0f}; diff --git a/src/game_api/entities_chars.cpp b/src/game_api/entities_chars.cpp index 099065e9f..02b651b70 100644 --- a/src/game_api/entities_chars.cpp +++ b/src/game_api/entities_chars.cpp @@ -68,7 +68,7 @@ uint8_t Player::kapala_blood_amount() void Player::set_jetpack_fuel(uint8_t fuel) { static auto jetpackID = to_id("ENT_TYPE_ITEM_JETPACK"); - for (auto item : items.entities()) + for (auto item : items.entities()) // TODO: should probably change to powerups right? { if (item->type->id == jetpackID) { diff --git a/src/game_api/entities_chars.hpp b/src/game_api/entities_chars.hpp index f326d5a51..863161149 100644 --- a/src/game_api/entities_chars.hpp +++ b/src/game_api/entities_chars.hpp @@ -7,14 +7,15 @@ #include // for u16string, allocator #include // for vector -#include "aliases.hpp" // for ENT_TYPE -#include "color.hpp" // for Color -#include "movable.hpp" // for Movable +#include "aliases.hpp" // for ENT_TYPE +#include "containers/custom_map.hpp" // +#include "movable.hpp" // for Movable struct Illumination; struct PlayerInputs; struct Inventory; struct PlayerSlot; +struct Color; class Entity; class Ai @@ -60,7 +61,7 @@ class Ai class PowerupCapable : public Movable { public: - std::map powerups; // type id -> entity + custom_map powerups; // type id -> entity /// Removes a currently applied powerup. Specify `ENT_TYPE.ITEM_POWERUP_xxx`, not `ENT_TYPE.ITEM_PICKUP_xxx`! Removing the Eggplant crown does not seem to undo the throwing of eggplants, the other powerups seem to work. void remove_powerup(ENT_TYPE powerup_type); diff --git a/src/game_api/entities_floors.cpp b/src/game_api/entities_floors.cpp index b889524bb..4d8cd54a6 100644 --- a/src/game_api/entities_floors.cpp +++ b/src/game_api/entities_floors.cpp @@ -801,7 +801,7 @@ void Door::unlock(bool unlock) { if (!main_door->door_blocker) { - main_door->door_blocker = state.layer_local(layer)->spawn_entity_over(door_bg_large, this, 0, 2.0); + main_door->door_blocker = state.layer(layer)->spawn_entity_over(door_bg_large, this, 0, 2.0); main_door->door_blocker->animation_frame = 1; } } diff --git a/src/game_api/entities_items.hpp b/src/game_api/entities_items.hpp index a95b00a3c..b4e24d141 100644 --- a/src/game_api/entities_items.hpp +++ b/src/game_api/entities_items.hpp @@ -152,7 +152,7 @@ class Flame : public Movable class FlameSize : public Flame { public: - /// if changed, gradually goes down (0.03 per frame) to the default size + /// if changed, gradually goes down (0.03 per frame) to the default size, it's the base value for `entity.width` and `entity.height` float flame_size; }; diff --git a/src/game_api/entity_lookup.cpp b/src/game_api/entity_lookup.cpp index 5b9273b0b..3e9b609fb 100644 --- a/src/game_api/entity_lookup.cpp +++ b/src/game_api/entity_lookup.cpp @@ -102,7 +102,7 @@ void foreach_mask(uint32_t mask, Layer* l, FunT&& fun) std::vector get_entities_by(std::vector entity_types, uint32_t mask, LAYER layer) { - auto state = State::get(); + auto state = State::get().ptr(); std::vector found; const std::vector proper_types = get_proper_types(std::move(entity_types)); @@ -124,25 +124,27 @@ std::vector get_entities_by(std::vector entity_types, uint32 if (layer == LAYER::BOTH) { + auto layer_front = state->layers[0]; + auto layer_back = state->layers[1]; if (proper_types.empty() || proper_types[0] == 0) { if (mask == 0) // all entities { // this exception for small improvments with calling reserve once - found.reserve(found.size() + (size_t)state.layer(0)->all_entities.size + (size_t)state.layer(1)->all_entities.size); - found.insert(found.end(), state.layer(0)->all_entities.uids().begin(), state.layer(0)->all_entities.uids().end()); - found.insert(found.end(), state.layer(1)->all_entities.uids().begin(), state.layer(1)->all_entities.uids().end()); + found.reserve(found.size() + (size_t)layer_front->all_entities.size + (size_t)layer_back->all_entities.size); + found.insert(found.end(), layer_front->all_entities.uids().begin(), layer_front->all_entities.uids().end()); + found.insert(found.end(), layer_back->all_entities.uids().begin(), layer_back->all_entities.uids().end()); } else // all types { - foreach_mask(mask, state.layer(0), insert_all_uids); - foreach_mask(mask, state.layer(1), insert_all_uids); + foreach_mask(mask, layer_front, insert_all_uids); + foreach_mask(mask, layer_back, insert_all_uids); } } else { - foreach_mask(mask, state.layer(0), push_matching_types); - foreach_mask(mask, state.layer(1), push_matching_types); + foreach_mask(mask, layer_front, push_matching_types); + foreach_mask(mask, layer_back, push_matching_types); } } else @@ -150,11 +152,11 @@ std::vector get_entities_by(std::vector entity_types, uint32 uint8_t correct_layer = enum_to_layer(layer); if (proper_types.empty() || proper_types[0] == 0) // all types { - foreach_mask(mask, state.layer(correct_layer), insert_all_uids); + foreach_mask(mask, state->layers[correct_layer], insert_all_uids); } else { - foreach_mask(mask, state.layer(correct_layer), push_matching_types); + foreach_mask(mask, state->layers[correct_layer], push_matching_types); } } return found; @@ -167,6 +169,7 @@ std::vector get_entities_by(ENT_TYPE entity_type, uint32_t mask, LAYER std::vector get_entities_at(std::vector entity_types, uint32_t mask, float x, float y, LAYER layer, float radius) { + // TODO: use entitie regions? auto state = State::get(); std::vector found; const std::vector proper_types = get_proper_types(std::move(entity_types)); @@ -175,7 +178,7 @@ std::vector get_entities_at(std::vector entity_types, uint32 for (auto& item : entities.entities()) { auto [ix, iy] = item->position(); - float distance = sqrt(pow(x - ix, 2.0f) + pow(y - iy, 2.0f)); + float distance = (float)std::sqrt(std::pow(x - ix, 2) + std::pow(y - iy, 2)); if (distance < radius && entity_type_check(proper_types, item->type->id)) { found.push_back(item->uid); @@ -201,6 +204,7 @@ std::vector get_entities_at(ENT_TYPE entity_type, uint32_t mask, float std::vector get_entities_overlapping_hitbox(std::vector entity_types, uint32_t mask, AABB hitbox, LAYER layer) { + // TODO: use entitie regions? auto state = State::get(); std::vector result; const std::vector proper_types = get_proper_types(std::move(entity_types)); diff --git a/src/game_api/fix_entity_descriptions.cpp b/src/game_api/fix_entity_descriptions.cpp deleted file mode 100644 index 1da36422b..000000000 --- a/src/game_api/fix_entity_descriptions.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include "fix_entity_descriptions.hpp" - -#include // for uint32_t -#include // for operator new -#include // for pair, min -#include // for allocator, vector, _Vector_const_iterator - -#include "entity.hpp" // for get_type, EntityDB - -// To update the description mapping, uncomment the #define below, and paste in the contents -// of the asset doc spreadsheet, run OL and it will dump the vector to stdout. Paste that over -// the vector at the bottom and it's up to date. - -// #define RECREATE_ENTITY_DESCRIPTIONS - -#ifdef RECREATE_ENTITY_DESCRIPTIONS -void recreate_entity_descriptions() -{ - std::string mapping_data = R"(ITEM_WHIP -ITEM_WHIP_FLAME -ITEM_BOMB -ITEM_PASTEBOMB -ITEM_ROPE -ITEM_CLIMBABLE_ROPE -ITEM_UNROLLED_ROPE -ITEM_BLOOD -ITEM_EGGSHIP -ITEM_PARENTSSHIP -ITEM_OLMECSHIP -ITEM_IDOL -ITEM_MADAMETUSK_IDOL -ITEM_MADAMETUSK_IDOLNOTE -ITEM_HOLDTHEIDOL -ITEM_TOTEM_SPEAR -ITEM_JUNGLE_SPEAR_COSMETIC -ITEM_JUNGLE_SPEAR_DAMAGING -ITEM_LION_SPEAR -ITEM_BIG_SPEAR -ITEM_ROCK -ITEM_WEB -ITEM_WEBSHOT -ITEM_GIANTSPIDER_WEBSHOT -ITEM_HANGSTRAND -ITEM_HANGANCHOR -ITEM_WOODEN_ARROW -ITEM_BROKEN_ARROW -ITEM_METAL_ARROW -ITEM_LIGHT_ARROW -ITEM_PLASMACANNON_SHOT -ITEM_SCEPTER_ANUBISSHOT -ITEM_SCEPTER_ANUBISSPECIALSHOT -ITEM_SCEPTER_PLAYERSHOT -ITEM_UFO_LASER_SHOT -ITEM_LAMASSU_LASER_SHOT -ITEM_SORCERESS_DAGGER_SHOT -ITEM_LASERTRAP_SHOT -ITEM_SPARK -ITEM_TIAMAT_SHOT -ITEM_FIREBALL -ITEM_HUNDUN_FIREBALL -ITEM_FLAMETHROWER_FIREBALL -ITEM_LEAF -ITEM_ACIDSPIT -ITEM_INKSPIT -ITEM_ACIDBUBBLE -ITEM_CRABMAN_ACIDBUBBLE -ITEM_CRABMAN_CLAW -ITEM_CRABMAN_CLAWCHAIN -ITEM_CHEST -ITEM_VAULTCHEST -ITEM_ENDINGTREASURE_TIAMAT -ITEM_ENDINGTREASURE_HUNDUN -ITEM_KEY -ITEM_LOCKEDCHEST -ITEM_LOCKEDCHEST_KEY -ITEM_CRATE -ITEM_DMCRATE -ITEM_TUTORIAL_MONSTER_SIGN -ITEM_CONSTRUCTION_SIGN -ITEM_SHORTCUT_SIGN -ITEM_SPEEDRUN_SIGN -ITEM_BASECAMP_TUTORIAL_SIGN -ITEM_BOOMBOX -ITEM_TV -ITEM_TELESCOPE -ITEM_WALLTORCH -ITEM_WALLTORCHFLAME -ITEM_LITWALLTORCH -ITEM_AUTOWALLTORCH -ITEM_TORCH -ITEM_TORCHFLAME -ITEM_LAMP -ITEM_LAMPFLAME -ITEM_REDLANTERN -ITEM_REDLANTERNFLAME -ITEM_PRESENT -ITEM_GHIST_PRESENT -ITEM_BULLET -ITEM_FREEZERAYSHOT -ITEM_CLONEGUNSHOT -ITEM_ICECAGE -ITEM_BROKEN_MATTOCK -ITEM_PUNISHBALL -ITEM_PUNISHCHAIN -ITEM_CHAIN -ITEM_CHAIN_LASTPIECE -ITEM_SLIDINGWALL_CHAIN -ITEM_SLIDINGWALL_CHAIN_LASTPIECE -ITEM_COFFIN -ITEM_FLY -ITEM_OLMECCANNON_BOMBS -ITEM_OLMECCANNON_UFO -ITEM_LANDMINE -ITEM_CURSING_CLOUD -ITEM_UDJAT_SOCKET -ITEM_USHABTI -ITEM_TURKEY_NECK -ITEM_HONEY -ITEM_GIANTCLAM_TOP -ITEM_PLAYERGHOST -ITEM_PLAYERGHOST_BREATH -ITEM_DIE -ITEM_DICE_BET -ITEM_DICE_PRIZE_DISPENSER -ITEM_LASERBEAM -ITEM_HORIZONTALLASERBEAM -ITEM_ANUBIS_COFFIN -ITEM_SPIKES -ITEM_EGGSHIP_HOOK -ITEM_AXOLOTL_BUBBLESHOT -ITEM_POTOFGOLD -ITEM_STICKYTRAP_PIECE -ITEM_STICKYTRAP_LASTPIECE -ITEM_STICKYTRAP_BALL -ITEM_SKULLDROPTRAP -ITEM_FROZEN_LIQUID -ITEM_ALIVE_EMBEDDED_ON_ICE -ITEM_DEPLOYED_PARACHUTE -ITEM_SLIDINGWALL_SWITCH -ITEM_SLIDINGWALL_SWITCH_REWARD -ITEM_GIANTFLY_HEAD -ITEM_PALACE_CANDLE_FLAME -ITEM_SNAP_TRAP -ITEM_EMPRESS_GRAVE -ITEM_TENTACLE -ITEM_TENTACLE_PIECE -ITEM_TENTACLE_LAST_PIECE -ITEM_MINIGAME_SHIP -ITEM_MINIGAME_UFO -ITEM_MINIGAME_ASTEROID_BG -ITEM_MINIGAME_ASTEROID -ITEM_MINIGAME_BROKEN_ASTEROID -ITEM_POT -ITEM_CURSEDPOT -ITEM_SKULL -ITEM_BONES -ITEM_COOKFIRE -ITEM_LAVAPOT -ITEM_SCRAP -ITEM_EGGPLANT -ITEM_ICESPIRE -ITEM_PALACE_CANDLE -ITEM_SKULLDROPTRAP_SKULL -ITEM_FLOATING_ORB -ITEM_EGGSAC -ITEM_GOLDBAR -ITEM_GOLDBARS -ITEM_DIAMOND -ITEM_EMERALD -ITEM_SAPPHIRE -ITEM_RUBY -ITEM_NUGGET -ITEM_GOLDCOIN -ITEM_EMERALD_SMALL -ITEM_SAPPHIRE_SMALL -ITEM_RUBY_SMALL -ITEM_NUGGET_SMALL -ITEM_PICKUP_TORNJOURNALPAGE -ITEM_PICKUP_JOURNAL -ITEM_PICKUP_ROPE -ITEM_PICKUP_ROPEPILE 1342 -ITEM_PICKUP_BOMBBAG 1343 -ITEM_PICKUP_BOMBBOX 1344 -ITEM_PICKUP_ROYALJELLY 1355 -ITEM_PICKUP_COOKEDTURKEY -ITEM_PICKUP_GIANTFOOD -ITEM_PICKUP_ELIXIR -ITEM_PICKUP_CLOVER -ITEM_PICKUP_SEEDEDRUNSUNLOCKER -ITEM_PICKUP_SPECTACLES 1345 -ITEM_PICKUP_CLIMBINGGLOVES 1346 -ITEM_PICKUP_PITCHERSMITT 1347 -ITEM_PICKUP_SPRINGSHOES 1348 -ITEM_PICKUP_SPIKESHOES 1349 -ITEM_PICKUP_PASTE 1350 -ITEM_PICKUP_COMPASS 1351 -ITEM_PICKUP_SPECIALCOMPASS -ITEM_PICKUP_PARACHUTE 1352 -ITEM_PICKUP_UDJATEYE -ITEM_PICKUP_KAPALA -ITEM_PICKUP_HEDJET 1353 -ITEM_PICKUP_CROWN -ITEM_PICKUP_EGGPLANTCROWN -ITEM_PICKUP_TRUECROWN -ITEM_PICKUP_ANKH -ITEM_PICKUP_TABLETOFDESTINY -ITEM_PICKUP_SKELETON_KEY 1354 -ITEM_PICKUP_PLAYERBAG -ITEM_POWERUP_PASTE -ITEM_POWERUP_CLIMBING_GLOVES -ITEM_POWERUP_SPIKE_SHOES -ITEM_POWERUP_SPRING_SHOES -ITEM_POWERUP_KAPALA -ITEM_POWERUP_SPECTACLES -ITEM_POWERUP_PITCHERSMITT -ITEM_POWERUP_UDJATEYE -ITEM_POWERUP_PARACHUTE -ITEM_POWERUP_COMPASS -ITEM_POWERUP_SPECIALCOMPASS -ITEM_POWERUP_HEDJET -ITEM_POWERUP_CROWN -ITEM_POWERUP_EGGPLANTCROWN -ITEM_POWERUP_TRUECROWN -ITEM_POWERUP_ANKH -ITEM_POWERUP_TABLETOFDESTINY -ITEM_POWERUP_SKELETON_KEY -ITEM_CAPE -ITEM_VLADS_CAPE -ITEM_PURCHASABLE_CAPE -ITEM_JETPACK - -ITEM_JETPACK_MECH -ITEM_PURCHASABLE_JETPACK 1356 -ITEM_TELEPORTER_BACKPACK -ITEM_PURCHASABLE_TELEPORTER_BACKPACK -ITEM_HOVERPACK -ITEM_PURCHASABLE_HOVERPACK -ITEM_POWERPACK -ITEM_PURCHASABLE_POWERPACK -ITEM_WEBGUN -ITEM_SHOTGUN -ITEM_FREEZERAY -ITEM_CROSSBOW -ITEM_CAMERA -ITEM_TELEPORTER -ITEM_MATTOCK -ITEM_BOOMERANG -ITEM_MACHETE -ITEM_EXCALIBUR -ITEM_BROKENEXCALIBUR -ITEM_PLASMACANNON -ITEM_SCEPTER -ITEM_CLONEGUN -ITEM_HOUYIBOW -ITEM_WOODEN_SHIELD -ITEM_METAL_SHIELD )"; - - const std::regex line_regex(R"(^([A-Z_]+)\s+([0-9]+)$)"); - auto ss = std::stringstream(mapping_data); - std::smatch m; - std::cout << "const std::vector> description_mapping = { \n"; - for (std::string line; std::getline(ss, line, '\n');) - { - if (std::regex_match(line, m, line_regex)) - { - auto entity_id = to_id(fmt::format("ENT_TYPE_{}", m.str(1))); - if (entity_id != -1) - { - std::cout << "{" << entity_id << ", " << m.str(2) << "},\n"; - } - else - { - DEBUG("Entity type {} is unknown", m.str(1)); - } - } - } - std::cout << "};\n\n"; -} -#endif - -void fix_entity_descriptions(STRINGID invalid_string_id) -{ -#ifdef RECREATE_ENTITY_DESCRIPTIONS - recreate_entity_descriptions(); -#endif - - const std::vector> description_mapping = { - {512, 1342}, - {513, 1343}, - {514, 1344}, - {517, 1355}, - {524, 1345}, - {525, 1346}, - {526, 1347}, - {527, 1348}, - {528, 1349}, - {529, 1350}, - {530, 1351}, - {532, 1352}, - {535, 1353}, - {541, 1354}, - {569, 1356}, - }; - - for (const auto& [entity_id, description_id] : description_mapping) - { - auto type = get_type(entity_id); - if (type != nullptr && type->description == invalid_string_id) - { - type->description = description_id; - } - } -} diff --git a/src/game_api/fix_entity_descriptions.hpp b/src/game_api/fix_entity_descriptions.hpp deleted file mode 100644 index d44314398..000000000 --- a/src/game_api/fix_entity_descriptions.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "aliases.hpp" - -void fix_entity_descriptions(STRINGID invalid_string_id); diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index 60cbf73e2..d13b09060 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -1,4 +1,5 @@ -const char* themes[]{ + +std::array themes{ "1: Dwelling", "2: Jungle", "2: Volcana", @@ -18,7 +19,7 @@ const char* themes[]{ "0: Base camp", }; -const char* themes_short[]{ +std::array themes_short{ "Dwelling", "Jungle", "Volcana", @@ -38,343 +39,325 @@ const char* themes_short[]{ "Base camp", }; -const char* entity_flags[]{ - "1: Invisible", - "2: Indestructable/special floor", - "3: Solid (wall)", - "4: Passes through objects", - "5: Passes through everything", - "6: Take no damage", - "7: Throwable/Knockbackable", - "8: Is platform", - "9: Climbable (as rope/ladder)", - "10: No gravity", - "11: Interact with liquid", - "12: Stunnable", - "13: Collides walls", - "14: Interact with semisolids?", - "15: Can be stomped", - "16: Power stomps", - "17: Facing left", - "18: Pickupable", - "19: Usable item?", - "20: Enable button prompt", - "21: Interact with webs", - "22: Can be carried through exit", - "23: Shop item", - "24: Shop floor (break to aggro)", - "25: Passes through player", - "26: Exploded", - "27: Crushed", - "28: Pause AI and physics", - "29: Dead", - "30: Character", - "31: Movable", - "32: Use overlay draw depth", +std::array entity_flags{ + "Invisible", + "Indestructable/special floor", + "Solid (wall)", + "Passes through objects", + "Passes through everything", + "Take no damage", + "Throwable/Knockbackable", + "Is platform", + "Climbable (as rope/ladder)", + "No gravity", + "Interact with liquid", + "Stunnable", + "Collides walls", + "Interact with semisolids?", + "Can be stomped", + "Power stomps", + "Facing left", + "Pickupable", + "Usable item?", + "Enable button prompt", + "Interact with webs", + "Can be carried through exit", + "Shop item", + "Shop floor (break to aggro)", + "Passes through player", + "Exploded", + "Crushed", + "Pause AI and physics", + "Dead", + "Character", + "Movable", + "Use overlay draw depth", }; -const char* more_flags[]{ - "1: Take damage on collision after throw", - "2: Revived (HH)", - "3: Blocks shield", - "4: Passes through shield", - "5: Being moved by shield", - "6: Just spawned?", - "7: Done spawning?", - "8: Stuck in something", - "9: Stuck in something, 1 frame later", - "10: Pitchers mitt antigravity", - "11: Swimming", - "12: Hit ground", - "13: Hit wall", - "14: Falling", - "15: Cursed effect", - "16: Elixir buff", - "17: Disable input", - "18: Shop item held by player", - "19: Falling platform something", - "20: Yangs turkey", - "21: Floor can be decorated", - "22: Disable collision", - "23: JumpWhipped to shorthop", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array more_flags{ + "Take damage on collision after throw", + "Revived (HH)", + "Blocks shield", + "Passes through shield", + "Being moved by shield", + "Just spawned?", + "Done spawning?", + "Stuck in something", + "Stuck in something, 1 frame later", + "Pitchers mitt antigravity", + "Swimming", + "Hit ground", + "Hit wall", + "Falling", + "Cursed effect", + "Elixir buff", + "Disable input", + "Shop item held by player", + "Falling platform something", + "Yangs turkey", + "Floor can be decorated", + "Disable collision", + "JumpWhipped to shorthop", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; -const char* entity_type_properties_flags[]{ - "1: Apply solid block beautification", - "2: Treat as FLOORSTYLED", - "3: ", - "4: ", - "5: Squirts blood on damage", - "6: Climbable (as wall)", - "7: ", - "8: Fireproof", - "9: Double fire and explosive damage", - "10: Is on fire/lights on fire", - "11: Poison immunity", - "12: Poisons arrows when hit", - "13: Curse immunity", - "14: Cursed pot effect (curses you when destroyed closeby)", - "15: ", - "16: ", - "17: ", - "18: Can be carried through layer doors", - "19: ", - "20: ", - "21: Can spawn monsters on top", - "22: ", - "23: Can be cloned", - "24: Can be bubbled", - "25: Can be telefragged", - "26: Disable updates?", - "27: Kill entity when overlay lost?", - "28: Unused", - "29: Unused", - "30: Unused", - "31: Unused", - "32: Unused", +std::array entity_type_properties_flags{ + "Apply solid block beautification", + "Treat as FLOORSTYLED", + "Unknown", + "Unknown", + "Squirts blood on damage", + "Climbable (as wall)", + "Unknown", + "Fireproof", + "Double fire and explosive damage", + "Is on fire/lights on fire", + "Poison immunity", + "Poisons arrows when hit", + "Curse immunity", + "Cursed pot effect (curses you when destroyed closeby)", + "Unknown", + "Unknown", + "Unknown", + "Can be carried through layer doors", + "Unknown", + "Unknown", + "Can spawn monsters on top", + "Unknown", + "Can be cloned", + "Can be bubbled", + "Can be telefragged", + "Disable updates?", + "Kill entity when overlay lost?", + "", + "", + "", + "", + "", }; -const char* level_flags[]{ - "1: Upbeat dwelling intro music played", - "2: ", - "3: Running tutorial speedrun", - "4: Level has pet", - "5: Level has Tun/shop?", - "6: Tun killed?", - "7: Ghost pot level?", - "8: Dead are restless", - "9: Bees/Metal clanking/Oppression", - "10: Angry shopkeeper", - "11: Angry Tun", - "12: Angry caveman shopkeeper", - "13: Angry ghist shopkeeper", - "14: Angry Yang", - "15: Angry Tusk", - "16: Angry Waddler", - "17: Shop level?", - "18: Dark level (draw halo)", - "19: Altar destroyed", - "20: Allow pause", - "21: Hide hud, transition", - "22: Hide hud, camp", - "23: Have clover", - "24: Show forgiveness toast", - "25: ", - "26: Daily challenge options", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array level_flags{ + "Upbeat dwelling intro music played", + "Unknown", + "Running tutorial speedrun", + "Level has pet", + "Level has Tun/shop?", + "Tun killed?", + "Ghost pot level?", + "Dead are restless", + "Bees/Metal clanking/Oppression", + "Angry shopkeeper", + "Angry Tun", + "Angry caveman shopkeeper", + "Angry ghist shopkeeper", + "Angry Yang", + "Angry Tusk", + "Angry Waddler", + "Shop level?", + "Dark level (draw halo)", + "Altar destroyed", + "Allow pause", + "Hide hud, transition", + "Hide hud, camp", + "Have clover", + "Show forgiveness toast", + "Unknown", + "Daily challenge options", + "", + "", + "", + "", + "", + "", }; -const char* journal_flags[]{ - "1: I was a pacifist", - "2: I was a vegan", - "3: I was a vegetarian", - "4: I was a petty criminal", - "5: I was a wanted criminal", - "6: I was a crime lord", - "7: I was a king", - "8: I was a queen", - "9: I was a fool", - "10: I was an eggplant", - "11: I didn't care for treasure", - "12: I liked pets", - "13: I loved pets", - "14: I took damage", - "15: I survived death once", - "16: I slayed Kingu", - "17: I slayed Osiris", - "18: I defeated Tiamat", - "19: I defeated Hundun", - "20: I became one with the Cosmos", - "21: I eventually died", - "22: ", - "23: ", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array journal_flags{ + "I was a pacifist", + "I was a vegan", + "I was a vegetarian", + "I was a petty criminal", + "I was a wanted criminal", + "I was a crime lord", + "I was a king", + "I was a queen", + "I was a fool", + "I was an eggplant", + "I didn't care for treasure", + "I liked pets", + "I loved pets", + "I took damage", + "I survived death once", + "I slayed Kingu", + "I slayed Osiris", + "I defeated Tiamat", + "I defeated Hundun", + "I became one with the Cosmos", + "I eventually died", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; -const char* quest_flags[]{ - "1: Reset", - "2: Dark level spawned in world", - "3: Vault spawned in world", - "4: Next level has shopkeeper outpost (if possible)", - "5: Shop spawned", - "6: Shortcut used", - "7: Seeded mode", - "8: Daily challenge mode", - "9: Caveman Shopkeeper aggroed", - "10: Waddler aggroed", - "11: Shop bought out", - "12: Eggplant crown picked up", - "13: ", - "14: ", - "15: ", - "16: ", - "17: Udjat eye spawned", - "18: Black market spawned", - "19: Drill spawned", - "20: ", - "21: ", - "22: ", - "23: ", - "24: ", - "25: Moon challenge spawned", - "26: Star challenge spawned", - "27: Sun challenge spawned", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array quest_flags{ + "Reset", + "Dark level spawned in world", + "Vault spawned in world", + "Next level has shopkeeper outpost (if possible)", + "Shop spawned", + "Shortcut used", + "Seeded mode", + "Daily challenge mode", + "Caveman Shopkeeper aggroed", + "Waddler aggroed", + "Shop bought out", + "Eggplant crown picked up", + "", + "", + "", + "", + "Udjat eye spawned", + "Black market spawned", + "Drill spawned", + "", + "", + "", + "", + "", + "Moon challenge spawned", + "Star challenge spawned", + "Sun challenge spawned", + "", + "", + "", + "", + "", }; -const char* presence_flags[]{ - "1: Udjat eye", - "2: Black market", - "3: Vlad's castle/drill", - "4: ", - "5: ", - "6: ", - "7: ", - "8: ", - "9: Moon challenge", - "10: Star challenge", - "11: Sun challenge", - "12: ", - "13: ", - "14: ", - "15: ", - "16: ", - "17: ", - "18: ", - "19: ", - "20: ", - "21: ", - "22: ", - "23: ", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array presence_flags{ + "Udjat eye", + "Black market", + "Vlad's castle/drill", + "", + "", + "", + "", + "", + "Moon challenge", + "Star challenge", + "Sun challenge", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; -const char* illumination_flags[]{ - // this is actually two 8bit flags as they have different behaviors - "1: Disable light1", - "2: Enable light2", - "3: Enable light3", - "4: Enable light4", - "5: ", - "6: ", - "7: Modulate brightness_multiplier", - "8: ", - // type_flags 1-8: - "9: Follow camera", - "10: Follow entity", - "11: Static, Rectangle, Full brightness", - "12: unused", - "13: unused", - "14: unused", - "15: unused", - "16: unused", +std::array illumination_flags{ + "Disable light1", + "Enable light2", + "Enable light3", + "Enable light4", + "Unknown", + "Unknown", // always on by default + "Modulate brightness_multiplier", + "Unknown", }; -const char* special_visibility_flags[]{ - "1: Crust embedded items shown", - "2: Crust embedded items shown (level transition)", - "3: ", - "4: ", - "5: ", - "6: ", - "7: ", - "8: ", - "9: Compass door markers shown", - "10: Compass door markers shown (level transition)", - "11: ", - "12: ", - "13: ", - "14: ", - "15: ", - "16: ", - "17: Special compass door markers shown", - "18: Special compass door markers shown (level transition)", - "19: ", - "20: ", - "21: ", - "22: ", - "23: ", - "24: ", - "25: In back layer", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array special_visibility_flags{ + "Crust embedded items shown", + "Crust embedded items shown (level transition)", + "", + "", + "", + "", + "", + "", + "Compass door markers shown", + "Compass door markers shown (level transition)", + "", + "", + "", + "", + "", + "", + "Special compass door markers shown", + "Special compass door markers shown (level transition)", + "", + "", + "", + "", + "", + "", }; -const char* basecamp_dialogue_win_flags[]{ - "1: Ana Spelunky saved", - "2: Margaret Tunnel saved", - "3: Colin Northward saved", - "4: Roffy D. Sloth saved", - "5: Alto Singh saved", - "6: Liz Mutton saved", - "7: Nekka The Eagle saved", - "8: LISE project saved", - "9: Coco Von Diamonds saved", - "10: Manfred Tunnel saved", - "11: Little Jay saved", - "12: Tina Flan saved", - "13: Valerie Crump saved", - "14: Au saved", - "15: Demi Von Diamonds saved", - "16: Pilot saved", - "17: Princess Airyn saved", - "18: Dirk Yamaoka saved", - "19: Tiamat win", - "20: Hundun win", - "21: ", - "22: ", - "23: ", - "24: ", - "25: ", - "26: ", - "27: ", - "28: ", - "29: ", - "30: ", - "31: ", - "32: ", +std::array basecamp_dialogue_win_flags{ + "Ana Spelunky saved", + "Margaret Tunnel saved", + "Colin Northward saved", + "Roffy D. Sloth saved", + "Alto Singh saved", + "Liz Mutton saved", + "Nekka The Eagle saved", + "LISE project saved", + "Coco Von Diamonds saved", + "Manfred Tunnel saved", + "Little Jay saved", + "Tina Flan saved", + "Valerie Crump saved", + "Au saved", + "Demi Von Diamonds saved", + "Pilot saved", + "Princess Airyn saved", + "Dirk Yamaoka saved", + "Tiamat win", + "Hundun win", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", }; -const char* places_flags[]{ +std::array places_flags{ "Dwelling", "Jungle", "Volcana", @@ -393,7 +376,7 @@ const char* places_flags[]{ "Cosmic Ocean", }; -const char* people_flags[]{ +std::array people_flags{ "Ana Spelunky", "Margaret Tunnel", "Colin Northward", @@ -434,7 +417,7 @@ const char* people_flags[]{ "Eggplant King", }; -const char* bestiary_flags[]{ +std::array bestiary_flags{ "Snake", "Spider", "Bat", @@ -515,7 +498,7 @@ const char* bestiary_flags[]{ "Mech Rider", }; -const char* items_flags[]{ +std::array items_flags{ "Rope Pile", "Bomb Bag", "Bomb Box", @@ -572,7 +555,7 @@ const char* items_flags[]{ "Four-Leaf Clover", }; -const char* traps_flags[]{ +std::array traps_flags{ "Spikes", "Arrow Trap", "Totem Trap", @@ -599,7 +582,7 @@ const char* traps_flags[]{ "Egg Sac", }; -const char* shortcut_flags[]{ +std::array shortcut_flags{ "None", "Met Terra", "1-4: $2,000", @@ -613,41 +596,7 @@ const char* shortcut_flags[]{ "5-1: Golden Key (Unlocked)", }; -/*const char *empty_flags[]{ - "1: ", -"2: ", -"3: ", -"4: ", -"5: ", -"6: ", -"7: ", -"8: ", -"9: ", -"10: ", -"11: ", -"12: ", -"13: ", -"14: ", -"15: ", -"16: ", - "17: ", -"18: ", -"19: ", -"20: ", -"21: ", -"22: ", -"23: ", -"24: ", -"25: ", -"26: ", -"27: ", -"28: ", -"29: ", -"30: ", -"31: ", -"32: ",};*/ - -const char* button_flags[]{ +std::array button_flags{ "Jp", "Wp", "Bm", @@ -655,14 +604,14 @@ const char* button_flags[]{ "Rn", "Dr", }; -const char* direction_flags[]{ +std::array direction_flags{ "Left", "Down", "Up", "Right", }; -const char* liquid_pool_names[]{ +std::array liquid_pool_names{ "Water", "Coarse Water", "Lava", @@ -670,7 +619,7 @@ const char* liquid_pool_names[]{ "Stagnant Lava", }; -const char* mask_names[]{ +std::array mask_names{ "Player", "Mount", "Monster", @@ -688,7 +637,7 @@ const char* mask_names[]{ "Lava", }; -const char* char_states[]{ +std::array char_states{ "Flailing", "Standing", "Sitting", @@ -714,7 +663,7 @@ const char* char_states[]{ "Dying", }; -const char* screen_names[]{ +std::array screen_names{ "Logo", "Intro", "Prologue", @@ -747,7 +696,7 @@ const char* screen_names[]{ "Online Lobby", }; -const char* pause_types[]{ +std::array pause_types{ "1: Menu", "2: Fade (janky camera, default)", "4: Cutscene", @@ -757,40 +706,40 @@ const char* pause_types[]{ "Freeze on PRE_UPDATE", // this is not a real state.pause flag, it's only used by ui.cpp for magic }; -const char* levelgen_flags[]{ - "1: Should generate path", - "2: Can spawn vault", - "3: Can spawn shops", - "4: Can have outpost?", - "5: Should spawn hard floor decorations", - "6: Apply ambient occlusion", - "7: Should spawn behind-floor and below-floorstyled decorations", - "8: unknown", +std::array levelgen_flags{ + "Should generate path", + "Can spawn vault", + "Can spawn shops", + "Can have outpost?", + "Should spawn hard floor decorations", + "Apply ambient occlusion", + "Should spawn behind-floor and below-floorstyled decorations", + "Unknown", }; -const char* levelgen_flags2[]{ - "1: Spawns background decorations on ground (ceiling if false)", - "2: Spawns fake ladder/chain midbg?", - "3: Spawn entrance door background (Ignored in 7-1 to 7-2 transition)", - "4: Procedural backlayer door midbg indicator related", - "5: Spawn backlayer border/background", - "6: Should spawn procedural backlayers", - "7: Should spawn backlayer torches", - "8: Has ghost", +std::array levelgen_flags2{ + "Spawns background decorations on ground (ceiling if false)", + "Spawns fake ladder/chain midbg?", + "Spawn entrance door background (Ignored in 7-1 to 7-2 transition)", + "Procedural backlayer door midbg indicator related", + "Spawn backlayer border/background", + "Should spawn procedural backlayers", + "Should spawn backlayer torches", + "Has ghost", }; -const char* levelgen_flags3[]{ - "1: Can spawn angry NPCs", - "2: Can echo", - "3: Can spawn Dead are Restless", - "4: Can spawn procedural skeletons", - "5: Can have quests?", - "6: Can spawn player coffins", - "7: unknown", - "8: unknown", +std::array levelgen_flags3{ + "Can spawn angry NPCs", + "Can echo", + "Can spawn Dead are Restless", + "Can spawn procedural skeletons", + "Can have quests?", + "Can spawn player coffins", + "Unknown", + "Unknown", }; -const char* level_chances[]{ +std::array level_chances{ "backroom", "backroom interconnect", "backroom hidden door", diff --git a/src/game_api/game_api.cpp b/src/game_api/game_api.cpp new file mode 100644 index 000000000..b4521058a --- /dev/null +++ b/src/game_api/game_api.cpp @@ -0,0 +1,35 @@ +#include "game_api.hpp" + +#include "render_api.hpp" +#include "search.hpp" +#include "state.hpp" + +GameAPI* GameAPI::get() +{ + using GetGameAPI = GameAPI*(); + static auto addr = (GetGameAPI*)get_address("get_game_api"); + return addr(); +} + +float GameAPI::get_current_zoom() +{ + auto state = State::get().ptr(); + return renderer->current_zoom + get_layer_transition_zoom_offset(state->camera_layer); +} + +float GameAPI::get_target_zoom() +{ + return renderer->target_zoom + renderer->target_zoom_offset; +} + +void GameAPI::set_zoom(std::optional current, std::optional target) +{ + if (current.has_value()) + { + renderer->current_zoom = current.value(); // - renderer->current_zoom_offset; + } + if (target.has_value()) + { + renderer->target_zoom = target.value(); // - renderer->target_zoom_offset; + } +} diff --git a/src/game_api/game_api.hpp b/src/game_api/game_api.hpp new file mode 100644 index 000000000..a8df03247 --- /dev/null +++ b/src/game_api/game_api.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct Renderer +{ + uint32_t render_width; // same as window size unless resolution scale is set + uint32_t render_height; + + uint32_t fps; // changing it doesn't seam to do anything + uint32_t fps_denominator; + + uint32_t render_width2; // repeat? + uint32_t render_height2; + + uint8_t flags1; + uint8_t flags2; + uint8_t padding[6]; + + uint8_t skip[0x1228]; // tons of pointers, some structs with vtables + + uint8_t skip2[0x7F048]; // a lot of nothing + + size_t unknown38; // bool? + float unknown39; // not sure if actually float + float unknown40; + float unknown41; + uint8_t unknown42[4]; + const char** unknown43a; // font/floor it's changing + const char** unknown43b; // noise0.dds + const char** unknown43c; // noise1.dds + size_t unknown44[4]; // null? + size_t unknown45; // bool? + + // feels like two standard containers or something + size_t* unknown46; + size_t* unknown47; + size_t unknown48; + size_t* unknown49; + size_t* unknown50; + size_t unknown51; // + + uint32_t unknown52; + uint32_t unknown53; // padding probably + size_t unknown54; // sometimes -1 sometimes pointer + uint32_t unknown55; // -1 + uint32_t unknown56; // null + size_t unknown57; + size_t unknown58; + uint16_t unknown59; // 2k + uint16_t unknown60a; // 512 + uint16_t unknown60b[2]; // padding? + size_t* unknown61[4]; + size_t unknown62; // bool? + std::unordered_map unknown63; // not sure about the key/value + + // bounch of vectors that probably used to load textures or something, they all seam to contain names of the .dds files + // when i checked all seam to be already cleared and just have the data leftover, the "const char**" pointers identical as in texturedb + + size_t unknown64[6]; // possibly two more vectors? + std::vector unknown65; // splash 0,1,2 + std::vector unknown66; // fonts, basecamp, pet + std::vector unknown67; // fonts and menu textures + characters (character select screen textures?) + std::vector unknown68; // main menu background textures? + size_t unknown69[3]; // probably also vector, but it's null when i checked + std::vector unknown70; // menu textures? + std::vector unknown71; // only the ai.dds + size_t unknown[8]; // null + + uint8_t unknown80; + uint8_t unknown81; + uint8_t unknown82; + uint8_t unknown83; // padding probably + float current_zoom; + float target_zoom; + float target_zoom_offset; + float current_zoom_offset; + float backlayer_light_level; // constantly overwritten by theme virtual get_backlayer_light_level + uint8_t unknown84; + uint8_t unknown85; + uint8_t unknown86[6]; // padding probably + + size_t* unknown87; // some vtables + + uint8_t skip3[0xAD8]; // probably some static arrays of ... stuff + + size_t swap_chain; + // 3 more pointers, some bit fields, then 5 more pointers + + // somewhere there should be shaders stored + + // added just to have the vtable + virtual ~Renderer() = 0; + virtual void some_dx_stuff() = 0; // it actually has a ton of parameters +}; + +struct GameAPI // size 0x60 +{ + static GameAPI* get(); + + float get_current_zoom(); + float get_target_zoom(); + + void set_zoom(std::optional current, std::optional target); + + bool unknown1; + size_t unknown2; // pointer + Renderer* renderer; + uint32_t window_width; + uint32_t window_height; + + size_t unknown5; // garbage? + size_t unknown6; // exe start + size_t unknown7; // some offset + size_t unknown8; // garbage? + size_t SteamAPI_Callback; // just vtable? don't know much about steam stuff + + uint8_t unknown10a; // bool ? + uint32_t unknown10b; + + size_t unknown11; // garbage? + size_t unknown12; // garbage? +}; diff --git a/src/game_api/illumination.cpp b/src/game_api/illumination.cpp new file mode 100644 index 000000000..02590c1b0 --- /dev/null +++ b/src/game_api/illumination.cpp @@ -0,0 +1,52 @@ +#include "illumination.hpp" + +#include "color.hpp" +#include "entity.hpp" +#include "math.hpp" +#include "search.hpp" +#include "state.hpp" +#include "thread_utils.hpp" +#include + +Illumination* create_illumination(Vec2 pos, Color col, LIGHT_TYPE type, float size, uint8_t light_flags, int32_t uid, LAYER layer) +{ + static size_t offset = get_address("generate_illumination"); + + if (offset != 0) + { + auto state = get_state_ptr(); + + typedef Illumination* create_illumination_func(custom_vector*, Vec2*, Color, LIGHT_TYPE, float, uint8_t light_flags, int32_t uid, uint8_t layer); + static create_illumination_func* cif = (create_illumination_func*)(offset); + auto emitted_light = cif(state->lightsources, &pos, std::move(col), type, size, light_flags, uid, enum_to_layer(layer)); + return emitted_light; + } + return nullptr; +} + +Illumination* create_illumination(Color color, float size, float x, float y) +{ + return create_illumination(Vec2{x, y}, std::move(color), LIGHT_TYPE::NONE, size, 0x20, -1, LAYER::FRONT); +} + +Illumination* create_illumination(Color color, float size, int32_t uid) +{ + auto entity = get_entity_ptr(uid); + if (entity != nullptr) + { + return create_illumination(Vec2{entity->abs_x, entity->abs_y}, std::move(color), LIGHT_TYPE::FOLLOW_ENTITY, size, 0x20, uid, (LAYER)entity->layer); + } + return nullptr; +} + +void refresh_illumination(Illumination* illumination) +{ + static uint32_t* offset = 0; + if (offset == 0) + { + size_t** heap_offset = (size_t**)get_address("refresh_illumination_heap_offset"); + auto illumination_counter = OnHeapPointer(**heap_offset); + offset = illumination_counter.decode(); + } + illumination->timer = *offset; +} diff --git a/src/game_api/illumination.hpp b/src/game_api/illumination.hpp new file mode 100644 index 000000000..7376ce473 --- /dev/null +++ b/src/game_api/illumination.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include "aliases.hpp" + +struct Color; +struct Vec2; + +enum class LIGHT_TYPE : uint8_t +{ + NONE = 0x0, + FOLLOW_CAMERA = 0x1, + FOLLOW_ENTITY = 0x2, + ROOM_LIGHT = 0x4, +}; + +struct LightParams // it's probably just Color +{ + float red; // default = 1.0 (can go over 1.0 for oversaturation) + float green; + float blue; + float size; + + /// Returns LightParams as Color, note that size = alpha + Color* as_color() + { + return reinterpret_cast(this); + }; +}; + +struct Illumination +{ + union + { + /// Table of light1, light2, ... etc. + std::array lights; + struct + { + LightParams light1; + LightParams light2; + LightParams light3; + /// It's rendered on objects around, not as an actual bright spot + LightParams light4; + }; + }; + float brightness; + float brightness_multiplier; + float light_pos_x; + float light_pos_y; + float offset_x; + float offset_y; + float distortion; + int32_t entity_uid; + uint32_t timer; + union + { + /// see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags + uint32_t flags; + struct + { + uint8_t light_flags; // not exposed since flags already is so no reason to essentially expose this again, even thou flags is technically not correct + + LIGHT_TYPE type_flags; + uint8_t layer; + bool enabled; + }; + }; +}; + +Illumination* create_illumination(Vec2 pos, Color color, LIGHT_TYPE type, float size, uint8_t flags, int32_t uid, LAYER layer); +Illumination* create_illumination(Color color, float size, float x, float y); +Illumination* create_illumination(Color color, float size, int32_t uid); +void refresh_illumination(Illumination* illumination); diff --git a/src/game_api/layer.cpp b/src/game_api/layer.cpp index 735f8cd2f..a6aed792c 100644 --- a/src/game_api/layer.cpp +++ b/src/game_api/layer.cpp @@ -149,7 +149,7 @@ Entity* Layer::spawn_apep(float x, float y, bool right) { static const auto head_id = to_id("ENT_TYPE_MONS_APEP_HEAD"); static const auto body_id = to_id("ENT_TYPE_MONS_APEP_BODY"); - static const auto facing_left_flag = 1 << 16; + constexpr auto facing_left_flag = 1 << 16; Entity* apep_head = spawn_entity(head_id, x, y, false, 0.0f, 0.0f, true); const bool facing_left = apep_head->flags & facing_left_flag; diff --git a/src/game_api/level_api.cpp b/src/game_api/level_api.cpp index 4b223b0bc..8da8a00a4 100644 --- a/src/game_api/level_api.cpp +++ b/src/game_api/level_api.cpp @@ -848,65 +848,6 @@ void level_gen(LevelGenSystem* level_gen_sys, float param_2, size_t param_3) g_levels_to_load.clear(); } -using TransGenFun = void(ThemeInfo*); -TransGenFun* g_trans_gen_trampoline{nullptr}; -TransGenFun* g_trans_gen2_trampoline{nullptr}; -using TransGenFun3 = void(size_t, size_t, ThemeInfo*); -TransGenFun3* g_trans_gen3_trampoline{nullptr}; -using TransGenFun4 = void(size_t, size_t, size_t, size_t, size_t, size_t); -TransGenFun4* g_trans_gen4_trampoline{nullptr}; -// generic transition hook -void trans_gen(ThemeInfo* theme) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - if (pre_level_generation()) - return; - g_trans_gen_trampoline(theme); - post_level_generation(); -} -// cosmic transition hook -void trans_gen2(ThemeInfo* theme) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - if (pre_level_generation()) - return; - g_trans_gen2_trampoline(theme); - post_level_generation(); -} -// cog-duat transition hook -void trans_gen3(size_t a, size_t b, ThemeInfo* theme) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - auto state = State::get().ptr(); - // trampoline will call the generic trans_gen if not going to duat - if (state->theme_next == 12 && pre_level_generation()) - return; - g_trans_gen3_trampoline(a, b, theme); - if (state->theme_next == 12) - post_level_generation(); -} -// olmecship transition hook -void trans_gen4(size_t a, size_t b, size_t c, size_t d, size_t e, size_t f) -{ - push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); - OnScopeExit pop{[] - { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; - - if (pre_level_generation()) - return; - g_trans_gen4_trampoline(a, b, c, d, e, f); - post_level_generation(); -} - using LoadScreenFun = void(StateMemory*, size_t, size_t); LoadScreenFun* g_load_screen_trampoline{nullptr}; void load_screen(StateMemory* state, size_t param_2, size_t param_3) @@ -1337,7 +1278,7 @@ bool handle_chance(SpawnInfo* spawn_info) auto level_gen_data = State::get().ptr()->level_gen->data; uint8_t layer = 0; - auto* layer_ptr = State::get().layer_local(layer); + auto* layer_ptr = State::get().layer(layer); for (const CommunityChance& community_chance : g_community_chances) { if (community_chance.test_func(community_chance, spawn_info->x, spawn_info->y, layer_ptr)) @@ -1560,10 +1501,6 @@ void LevelGenData::init() g_load_screen_trampoline = (LoadScreenFun*)get_address("load_screen_func"sv); g_unload_layer_trampoline = (UnloadLayerFun*)get_address("unload_layer"sv); g_init_layer_trampoline = (InitLayerFun*)get_address("init_layer"sv); - g_trans_gen_trampoline = (TransGenFun*)get_address("spawn_transition"sv); - g_trans_gen2_trampoline = (TransGenFun*)get_address("spawn_transition_cosmic"sv); - g_trans_gen3_trampoline = (TransGenFun3*)get_address("spawn_transition_duat"sv); - g_trans_gen4_trampoline = (TransGenFun4*)get_address("spawn_transition_olmecship"sv); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); @@ -1581,15 +1518,11 @@ void LevelGenData::init() DetourAttach((void**)&g_load_screen_trampoline, load_screen); DetourAttach((void**)&g_unload_layer_trampoline, unload_layer); DetourAttach((void**)&g_init_layer_trampoline, load_layer); - DetourAttach((void**)&g_trans_gen_trampoline, trans_gen); - DetourAttach((void**)&g_trans_gen2_trampoline, trans_gen2); - DetourAttach((void**)&g_trans_gen3_trampoline, trans_gen3); - DetourAttach((void**)&g_trans_gen4_trampoline, trans_gen4); const LONG error = DetourTransactionCommit(); if (error != NO_ERROR) { - DEBUG("Failed hooking HandleTileCode: {}\n", error); + DEBUG("Failed hooking LevelGenData stuff: {}\n", error); } } @@ -1731,7 +1664,7 @@ std::uint32_t LevelGenData::register_chance_logic_provider(std::uint32_t chance_ { provider.is_valid = [](float x, float y, uint8_t layer) { - return g_DefaultTestFunc(x, y, State::get().layer_local(layer)); + return g_DefaultTestFunc(x, y, State::get().layer(layer)); }; } @@ -1755,7 +1688,7 @@ std::uint32_t LevelGenData::define_extra_spawn(std::uint32_t num_spawns_front_la { provider.is_valid = [](float x, float y, uint8_t layer) { - return g_DefaultTestFunc(x, y, State::get().layer_local(layer)); + return g_DefaultTestFunc(x, y, State::get().layer(layer)); }; } @@ -1880,6 +1813,26 @@ uint32_t ThemeInfo::get_aux_id() void LevelGenSystem::init() { data->init(); + + for (auto theme : themes) + { + if (theme == theme_arena) // no reason to? + continue; + + hook_vtable( // spawn_transition + theme, + [](ThemeInfo* th, void (*original)(ThemeInfo*)) + { + push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); + OnScopeExit pop{[] + { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; + + if (pre_level_generation()) + return; + original(th); + post_level_generation(); + }); + } } void LevelGenSystem::populate_level_hook(ThemeInfo* self, uint64_t param_2, uint64_t param_3, uint64_t param_4, PopulateLevelFun* original) @@ -1897,12 +1850,15 @@ void LevelGenSystem::populate_level_hook(ThemeInfo* self, uint64_t param_2, uint original(self, param_2, param_3, param_4); } -void LevelGenSystem::populate_transition_hook(ThemeInfo* self, PopulateTransitionFun* original) -{ - pre_level_generation(); - original(self); - post_level_generation(); -} +// void LevelGenSystem::populate_transition_hook([[maybe_unused]] ThemeInfo* self, [[maybe_unused]] PopulateTransitionFun* original) +//{ +// push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); +// OnScopeExit pop{[] +// { pop_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_GENERAL); }}; +// pre_level_generation(); +// original(self); +// post_level_generation(); +// } void LevelGenSystem::do_procedural_spawn_hook(ThemeInfo* self, SpawnInfo* spawn_info, DoProceduralSpawnFun* original) { push_spawn_type_flags(SPAWN_TYPE_LEVEL_GEN_PROCEDURAL); @@ -2146,13 +2102,13 @@ bool LevelGenSystem::set_procedural_spawn_chance(uint32_t chance_id, uint32_t in bool default_spawn_is_valid(float x, float y, LAYER layer) { uint8_t correct_layer = enum_to_layer(layer); - return g_DefaultTestFunc(x, y, State::get().layer_local(correct_layer)); + return g_DefaultTestFunc(x, y, State::get().layer(correct_layer)); } bool position_is_valid(float x, float y, LAYER layer, POS_TYPE flags) { uint8_t correct_layer = enum_to_layer(layer); - return g_PositionTestFunc(x, y, State::get().layer_local(correct_layer), (uint32_t)flags); + return g_PositionTestFunc(x, y, State::get().layer(correct_layer), (uint32_t)flags); } void override_next_levels(std::vector next_levels) diff --git a/src/game_api/level_api.hpp b/src/game_api/level_api.hpp index 1e300b37a..32fd2e363 100644 --- a/src/game_api/level_api.hpp +++ b/src/game_api/level_api.hpp @@ -436,8 +436,8 @@ struct LevelGenSystem using PopulateLevelFun = void(ThemeInfo*, uint64_t, uint64_t, uint64_t); static void populate_level_hook(ThemeInfo*, uint64_t, uint64_t, uint64_t, PopulateLevelFun*); - using PopulateTransitionFun = void(ThemeInfo*); - static void populate_transition_hook(ThemeInfo*, PopulateTransitionFun*); + // using PopulateTransitionFun = void(ThemeInfo*); + // static void populate_transition_hook(ThemeInfo*, PopulateTransitionFun*); using DoProceduralSpawnFun = void(ThemeInfo*, SpawnInfo*); static void do_procedural_spawn_hook(ThemeInfo*, SpawnInfo*, DoProceduralSpawnFun*); diff --git a/src/game_api/math.cpp b/src/game_api/math.cpp index bae4186ea..e09db53b9 100644 --- a/src/game_api/math.cpp +++ b/src/game_api/math.cpp @@ -1,6 +1,6 @@ #include "math.hpp" -bool Triangle::is_point_inside(Vec2 p, float epsilon) const +bool Triangle::is_point_inside(const Vec2 p, float epsilon) const { // you can compare it eather by area or by angle // not sure if one if faster thne the order, so i left code for both @@ -35,19 +35,19 @@ Vec2 intersection(const Vec2 A, const Vec2 B, const Vec2 C, const Vec2 D) return Vec2{(b1 * c - b * c1) / det, (a * c1 - a1 * c) / det}; } -float two_lines_angle(Vec2 A, Vec2 common, Vec2 B) +float two_lines_angle(const Vec2 A, const Vec2 common, const Vec2 B) { Vec2 ab = common - B; Vec2 bc = A - common; return std::atan2((bc.y * ab.x - bc.x * ab.y), (bc.x * ab.x + bc.y * ab.y)); }; -float two_lines_angle(Vec2 line1_A, Vec2 line1_B, Vec2 line2_A, Vec2 line2_B) +float two_lines_angle(const Vec2 line1_A, const Vec2 line1_B, const Vec2 line2_A, const Vec2 line2_B) { return two_lines_angle(line1_A, intersection(line1_A, line1_B, line2_A, line2_B), line2_B); }; -bool Quad::is_point_inside(Vec2 p, float epsilon) const +bool Quad::is_point_inside(const Vec2 p, float epsilon) const { std::tuple points = *this; diff --git a/src/game_api/math.hpp b/src/game_api/math.hpp index 20470e419..7cf3593ec 100644 --- a/src/game_api/math.hpp +++ b/src/game_api/math.hpp @@ -41,6 +41,11 @@ struct Vec2 diff *= diff; // pow return (float)std::sqrt(diff.x + diff.y); } + Vec2& set(const Vec2& other) + { + *this = other; + return *this; + } Vec2 operator+(const Vec2& a) const { @@ -271,6 +276,11 @@ struct AABB return false; } + AABB& set(const AABB& other) + { + *this = other; + return *this; + } /* std::tuple split() {} // just for the autodoc @@ -369,11 +379,11 @@ struct Triangle { return std::abs((A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y)) / 2.0f); } - bool is_point_inside(Vec2 p) const + bool is_point_inside(const Vec2 p) const { return is_point_inside(p, 0.0001f); } - bool is_point_inside(Vec2 p, float epsilon) const; + bool is_point_inside(const Vec2 p, float epsilon) const; bool is_point_inside(float x, float y) const { return is_point_inside(Vec2{x, y}, 0.0001f); @@ -382,7 +392,11 @@ struct Triangle { return is_point_inside(Vec2{x, y}, epsilon); } - + Triangle& set(const Triangle& other) + { + *this = other; + return *this; + } /* // just for the autodoc /// Returns the corner points std::tuple split(); @@ -508,11 +522,11 @@ struct Quad return *this; } - bool is_point_inside(Vec2 p) const + bool is_point_inside(const Vec2 p) const { return is_point_inside(p, 0.00001f); } - bool is_point_inside(Vec2 p, float epsilon) const; + bool is_point_inside(const Vec2 p, float epsilon) const; bool is_point_inside(float x, float y) const { return is_point_inside(Vec2{x, y}, 0.00001f); @@ -521,6 +535,11 @@ struct Quad { return is_point_inside(Vec2{x, y}, epsilon); } + Quad& set(const Quad& other) + { + *this = other; + return *this; + } /* // just for the autodoc /// Returns the corners in order: bottom_left, bottom_right, top_right, top_left @@ -551,7 +570,7 @@ struct Quad Vec2 intersection(const Vec2 A, const Vec2 B, const Vec2 C, const Vec2 D); /// Mesures angle between two lines with one common point -float two_lines_angle(Vec2 A, Vec2 common, Vec2 B); +float two_lines_angle(const Vec2 A, const Vec2 common, const Vec2 B); /// Gets line1_A, intersection point and line2_B and calls the 3 parameter version of this function -float two_lines_angle(Vec2 line1_A, Vec2 line1_B, Vec2 line2_A, Vec2 line2_B); +float two_lines_angle(const Vec2 line1_A, const Vec2 line1_B, const Vec2 line2_A, const Vec2 line2_B); diff --git a/src/game_api/particles.cpp b/src/game_api/particles.cpp index e209831fa..c38ea89dc 100644 --- a/src/game_api/particles.cpp +++ b/src/game_api/particles.cpp @@ -8,7 +8,9 @@ #include // for unordered_map, _Umap_traits<>::allocator_type #include // for min, max +#include "entity.hpp" // #include "search.hpp" // for get_address +#include "state.hpp" // #include "texture.hpp" // for get_texture, Texture ParticleDB* particle_db_ptr() @@ -103,7 +105,7 @@ const Particle EmittedParticlesInfo::operator[](const size_type idx) const }; } -ParticleDB* get_particle_type(uint32_t id) +ParticleDB* get_particle_type(PARTICLEEMITTER id) { static std::unordered_map mapping = {}; if (mapping.size() == 0) @@ -150,3 +152,78 @@ const std::vector& list_particles() } return particles; } + +ParticleDB::ParticleDB(PARTICLEEMITTER particle_id) + : ParticleDB(*get_particle_type(particle_id)){}; + +ParticleEmitterInfo* generate_world_particles(PARTICLEEMITTER particle_emitter_id, uint32_t uid) +{ + static size_t offset = get_address("generate_world_particles"); + + if (offset != 0) + { + auto entity = get_entity_ptr(uid); + if (entity != nullptr) + { + auto state = get_state_ptr(); + typedef ParticleEmitterInfo* generate_particles_func(custom_vector*, uint32_t, Entity*); + static generate_particles_func* gpf = (generate_particles_func*)(offset); + return gpf(state->particle_emitters, particle_emitter_id, entity); + } + } + return nullptr; +} + +ParticleEmitterInfo* generate_screen_particles(PARTICLEEMITTER particle_emitter_id, float x, float y) +{ + static size_t offset = get_address("generate_screen_particles"); + + if (offset != 0) + { + typedef ParticleEmitterInfo* generate_particles_func(uint32_t, float, float, size_t); + static generate_particles_func* gpf = (generate_particles_func*)(offset); + return gpf(particle_emitter_id, x, y, 0); + } + return nullptr; +} + +void advance_screen_particles(ParticleEmitterInfo* particle_emitter) +{ + static size_t offset = get_address("advance_screen_particles"); + + if (offset != 0) + { + typedef void advance_particles_func(ParticleEmitterInfo*); + static advance_particles_func* apf = (advance_particles_func*)(offset); + apf(particle_emitter); + } +} + +void render_screen_particles(ParticleEmitterInfo* particle_emitter) +{ + static size_t offset = get_address("render_screen_particles"); + + if (offset != 0) + { + typedef void render_particles_func(ParticleEmitterInfo*, size_t, size_t, size_t); + static render_particles_func* rpf = (render_particles_func*)(offset); + rpf(particle_emitter, 0, 0, 0); + } +} + +void extinguish_particles(ParticleEmitterInfo* particle_emitter) +{ + // removing from state only applies to world emitters, but it just won't find the screen one in the vector, so no big deal + auto state = get_state_ptr(); + std::erase(*state->particle_emitters, particle_emitter); + + using generic_free_func = void(void*); + static generic_free_func* generic_free = (generic_free_func*)get_address("generic_free"); + + if (particle_emitter != nullptr) + { + generic_free(particle_emitter->emitted_particles.memory); + generic_free(particle_emitter->emitted_particles_back_layer.memory); + generic_free(particle_emitter); + } +} diff --git a/src/game_api/particles.hpp b/src/game_api/particles.hpp index 503fe8267..281531293 100644 --- a/src/game_api/particles.hpp +++ b/src/game_api/particles.hpp @@ -14,9 +14,11 @@ struct Texture; +using PARTICLEEMITTER = uint32_t; + struct ParticleDB { - uint32_t id; + PARTICLEEMITTER id; int32_t spawn_count_min; // minimum amount of particles to spawn in an iteration, actual value is random between this value and spawn_count (-1 = no lower bound, uses spawn_count) uint32_t spawn_count; // total amount of particles to spawn for 1 iteration (check with PETTING_PET or MOUNT_TAMED, amount of hearts shown) int32_t lifespan_min; // minimum lifespan of a particle, actual value is random between this value and lifespan (-1 = no lower bound, uses lifespan) @@ -66,6 +68,9 @@ struct ParticleDB uint8_t unknown41; uint32_t unknown42; + ParticleDB(const ParticleDB& other) = default; + ParticleDB(const PARTICLEEMITTER particle_id); + std::uint64_t get_texture(); bool set_texture(std::uint32_t texture_id); }; @@ -104,8 +109,8 @@ struct EmittedParticlesInfo void* memory; uint16_t* max_lifetimes; uint16_t* lifetimes; - size_t unknown7; - size_t unknown8; + float* unknown7; + float* unknown8; float* x_positions; float* y_positions; float* unknown_x_positions; @@ -115,12 +120,12 @@ struct EmittedParticlesInfo float* heights; float* x_velocities; float* y_velocities; - size_t unknown18; - size_t unknown19; - size_t unknown20; - size_t unknown21; - size_t unknown22; - size_t unknown23; + uint8_t* unknown18; + uint8_t* unknown19; + uint8_t* unknown20; + uint8_t* unknown21; + uint8_t* unknown22; + uint8_t* unknown23; template class IteratorImpl : public neo::iterator_facade> @@ -200,10 +205,9 @@ struct ParticleEmitterInfo float offset_x; float offset_y; - uint8_t unknown54a; // layer? - uint8_t unknown54b; - uint8_t unknown54c; - uint8_t unknown54d; + uint8_t layer; + uint8_t draw_depth; + uint8_t padding_probably[2]; float unknown55; uint32_t unknown56; uint32_t total_particles; @@ -212,5 +216,10 @@ struct ParticleEmitterInfo uint32_t unknown60; }; -ParticleDB* get_particle_type(uint32_t id); +ParticleDB* get_particle_type(PARTICLEEMITTER id); const std::vector& list_particles(); +ParticleEmitterInfo* generate_world_particles(PARTICLEEMITTER particle_emitter_id, uint32_t uid); +ParticleEmitterInfo* generate_screen_particles(PARTICLEEMITTER particle_emitter_id, float x, float y); +void advance_screen_particles(ParticleEmitterInfo* particle_emitter); +void render_screen_particles(ParticleEmitterInfo* particle_emitter); +void extinguish_particles(ParticleEmitterInfo* particle_emitter); diff --git a/src/game_api/render_api.cpp b/src/game_api/render_api.cpp index 999632af5..407e357e2 100644 --- a/src/game_api/render_api.cpp +++ b/src/game_api/render_api.cpp @@ -12,6 +12,7 @@ #include // for vector #include "entity.hpp" // for Entity, EntityDB +#include "game_api.hpp" // #include "level_api.hpp" // for ThemeInfo #include "logger.h" // for DEBUG #include "memory.hpp" // for memory_read, to_le_bytes, write_mem_prot @@ -31,21 +32,20 @@ struct Layer; RenderAPI& RenderAPI::get() { - static RenderAPI render_api = []() - { - return RenderAPI{(size_t*)get_address("render_api_callback"sv), get_address("render_api_offset"sv)}; - }(); + static RenderAPI render_api; return render_api; } -size_t RenderAPI::renderer() const +Renderer* RenderAPI::renderer() const { - return memory_read(*api + 0x10); + auto game_api = GameAPI::get(); + return game_api->renderer; } size_t RenderAPI::swap_chain() const { - return memory_read(renderer() + swap_chain_off); + return renderer()->swap_chain; + // return memory_read(renderer() + swap_chain_off); // swap_chain_off from pattern: render_api_offset } void (*g_post_render_game)(){nullptr}; @@ -73,16 +73,9 @@ void render_layer(const std::vector& lightsources, uint8_t layer, if (trigger_vanilla_render_layer_callbacks(ON::RENDER_PRE_LAYER, layer)) return; - static size_t offset = 0; - if (offset == 0) - { - auto addr = State::get_zoom_level_address(); - offset = addr + 8; - } - if (offset != 0) - { - g_layer_zoom_offset[layer] = memory_read(offset); - } + auto game_api = GameAPI::get(); + g_layer_zoom_offset[layer] = game_api->renderer->current_zoom_offset; + // The lhs and rhs LUTs are blended in the shader, but we don't know where that value is CPU side so we can only override // with a single LUT for now if (g_forced_lut_textures[layer]) @@ -117,7 +110,7 @@ void render_game(StateMemory* state) trigger_vanilla_render_callbacks(ON::RENDER_POST_GAME); } -float get_layer_zoom_offset(uint8_t layer) +float get_layer_transition_zoom_offset(uint8_t layer) { return g_layer_zoom_offset[layer]; } @@ -482,7 +475,7 @@ void RenderAPI::draw_world_texture(Texture* texture, Quad source, Quad dest, Col dest.top_left_y, unknown}; - typedef void render_func(size_t, WorldShader, const char*** texture_name, uint32_t render_as_non_liquid, float* destination, Quad* source, void*, Color*, float*); + typedef void render_func(Renderer*, WorldShader, const char*** texture_name, uint32_t render_as_non_liquid, float* destination, Quad* source, void*, Color*, float*); static render_func* rf = (render_func*)(func_offset); auto texture_name = texture->name; rf(renderer(), shader, &texture_name, 1, destination, &source, (void*)param_7, &color, nullptr); @@ -500,7 +493,7 @@ void RenderAPI::set_advanced_hud() void RenderAPI::reload_shaders() { - using ReloadShadersFun = void(size_t); + using ReloadShadersFun = void(Renderer*); static ReloadShadersFun* reload_shaders_impl = (ReloadShadersFun*)get_address("reload_shaders"sv); reload_shaders_impl(renderer()); } diff --git a/src/game_api/render_api.hpp b/src/game_api/render_api.hpp index 47da31719..d0c90d1f7 100644 --- a/src/game_api/render_api.hpp +++ b/src/game_api/render_api.hpp @@ -18,6 +18,7 @@ struct JournalUI; struct Layer; class Entity; +struct Renderer; using VANILLA_TEXT_ALIGNMENT = uint32_t; using VANILLA_FONT_STYLE = uint32_t; @@ -225,9 +226,6 @@ struct TextureRenderingInfo struct RenderAPI { - const size_t* api; - size_t swap_chain_off; - mutable std::mutex custom_textures_lock; std::unordered_map custom_textures; std::unordered_map original_textures; @@ -236,7 +234,7 @@ struct RenderAPI static RenderAPI& get(); - size_t renderer() const; + Renderer* renderer() const; size_t swap_chain() const; void set_lut(TEXTURE texture_id, uint8_t layer); @@ -366,8 +364,8 @@ struct RenderInfo void init_render_api_hooks(); bool& get_journal_enabled(); void on_open_journal_chapter(JournalUI* journal_ui, uint8_t chapter, bool instant, bool play_sound); -float get_layer_zoom_offset(uint8_t layer); void render_draw_depth(Layer* layer, uint8_t draw_depth, float bbox_left, float bbox_bottom, float bbox_right, float bbox_top); +float get_layer_transition_zoom_offset(uint8_t layer); struct HudInventory { diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 3e39554dc..64ba19d26 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -20,28 +20,31 @@ #include // for _Uset_traits<>::allocator_type, _Use... #include // for min, max, pair, find -#include "custom_types.hpp" // for get_custom_entity_types, CUSTOM_TYPE -#include "entities_chars.hpp" // for Player (ptr only), PowerupCapable -#include "entities_floors.hpp" // for ExitDoor, Door -#include "entities_items.hpp" // for StretchChain, PunishBall, Container -#include "entities_liquids.hpp" // for Liquid -#include "entities_mounts.hpp" // for Mount -#include "entity.hpp" // for get_entity_ptr, to_id, Entity, EntityDB -#include "entity_lookup.hpp" // -#include "game_manager.hpp" // -#include "game_patches.hpp" // -#include "items.hpp" // for Items -#include "layer.hpp" // for EntityList, EntityList::Range, Layer -#include "logger.h" // for DEBUG -#include "math.hpp" // for AABB -#include "memory.hpp" // for write_mem_prot, write_mem_recoverable -#include "movable.hpp" // for Movable -#include "particles.hpp" // for ParticleEmitterInfo -#include "search.hpp" // for get_address, find_inst -#include "state.hpp" // for State, get_state_ptr, enum_to_layer -#include "state_structs.hpp" // for ShopRestrictedItem, Illumination -#include "thread_utils.hpp" // for OnHeapPointer -#include "virtual_table.hpp" // for get_virtual_function_address, VIRT_FUNC +#include "containers/custom_vector.hpp" // +#include "custom_types.hpp" // for get_custom_entity_types, CUSTOM_TYPE +#include "entities_chars.hpp" // for Player (ptr only), PowerupCapable +#include "entities_floors.hpp" // for ExitDoor, Door +#include "entities_items.hpp" // for StretchChain, PunishBall, Container +#include "entities_liquids.hpp" // for Liquid +#include "entities_mounts.hpp" // for Mount +#include "entity.hpp" // for get_entity_ptr, to_id, Entity, EntityDB +#include "entity_lookup.hpp" // +#include "game_manager.hpp" // +#include "game_patches.hpp" // +#include "illumination.hpp" // +#include "items.hpp" // for Items +#include "layer.hpp" // for EntityList, EntityList::Range, Layer +#include "logger.h" // for DEBUG +#include "math.hpp" // for AABB +#include "memory.hpp" // for write_mem_prot, write_mem_recoverable +#include "movable.hpp" // for Movable +#include "particles.hpp" // for ParticleEmitterInfo +#include "screen.hpp" // +#include "search.hpp" // for get_address, find_inst +#include "state.hpp" // for State, get_state_ptr, enum_to_layer +#include "state_structs.hpp" // for ShopRestrictedItem, Illumination +#include "thread_utils.hpp" // for OnHeapPointer +#include "virtual_table.hpp" // for get_virtual_function_address, VIRT_FUNC uint32_t setflag(uint32_t flags, int bit) // shouldn't we change those to #define ? { @@ -612,7 +615,7 @@ void unequip_backitem(uint32_t who_uid) int32_t worn_backitem(uint32_t who_uid) { - static const std::unordered_set backitem_types = { + static const auto backitem_types = { to_id("ENT_TYPE_ITEM_JETPACK"), to_id("ENT_TYPE_ITEM_HOVERPACK"), to_id("ENT_TYPE_ITEM_POWERPACK"), @@ -622,14 +625,13 @@ int32_t worn_backitem(uint32_t who_uid) }; auto ent = get_entity_ptr(who_uid)->as(); - if (ent != nullptr) + if (ent != nullptr && !ent->powerups.empty()) { - for (const auto& [powerup_type, powerup_entity] : ent->powerups) + for (auto powerup_type : backitem_types) { - if (backitem_types.count(powerup_type) > 0) - { - return powerup_entity->uid; - } + auto it = ent->powerups.find(powerup_type); + if (it != ent->powerups.end()) + return it->second->uid; } } return -1; @@ -757,6 +759,8 @@ void set_time_jelly_enabled(bool b) bool is_inside_active_shop_room(float x, float y, LAYER layer) { + // this functions just calculates the room index and then loops thru state->room_owners->owned_rooms and compares the room index + // TODO: we could probably get rid of this pattern and write that ourselves static size_t offset = get_address("coord_inside_active_shop_room"); if (offset != 0) { @@ -769,143 +773,23 @@ bool is_inside_active_shop_room(float x, float y, LAYER layer) bool is_inside_shop_zone(float x, float y, LAYER layer) { - static size_t offset = 0; - static void* rcx = nullptr; - if (offset == 0) - { - offset = get_address("coord_inside_shop_zone"); - size_t* tmp = (size_t*)get_address("coord_inside_shop_zone_rcx"); - auto heap_ptr = OnHeapPointer(*tmp); - rcx = heap_ptr.decode(); - } - if (offset != 0) - { - typedef bool coord_inside_shop_zone_func(void*, uint32_t layer, float x, float y); - static coord_inside_shop_zone_func* ciszf = (coord_inside_shop_zone_func*)(offset); - return ciszf(rcx, enum_to_layer(layer), x, y); - } - return false; -} + // this function is weird, the main check does this (where rax is the room_template): + // ecx = rax - 0x41 + // cmp cx, 0x17 + // ja return 0 + // + // if it doesn't jump there is a bunch of coordinate checks but also state.presence_flags, flipped rooms ... -ParticleEmitterInfo* generate_world_particles(uint32_t particle_emitter_id, uint32_t uid) -{ - static size_t offset = get_address("generate_world_particles"); + static size_t offset = get_address("coord_inside_shop_zone"); + auto state = State::get().ptr(); // the game gets level gen from heap pointer and we always get it from state, not sure if it matters if (offset != 0) { - auto entity = get_entity_ptr(uid); - if (entity != nullptr) - { - auto state = get_state_ptr(); - typedef ParticleEmitterInfo* generate_particles_func(std::vector*, uint32_t, Entity*); - static generate_particles_func* gpf = (generate_particles_func*)(offset); - return gpf(state->particle_emitters, particle_emitter_id, entity); - } + typedef bool coord_inside_shop_zone_func(LevelGenSystem*, uint32_t layer, float x, float y); + coord_inside_shop_zone_func* ciszf = (coord_inside_shop_zone_func*)(offset); + return ciszf(state->level_gen, enum_to_layer(layer), x, y); } - return nullptr; -} - -ParticleEmitterInfo* generate_screen_particles(uint32_t particle_emitter_id, float x, float y) -{ - static size_t offset = get_address("generate_screen_particles"); - - if (offset != 0) - { - typedef ParticleEmitterInfo* generate_particles_func(uint32_t, float, float, size_t); - static generate_particles_func* gpf = (generate_particles_func*)(offset); - return gpf(particle_emitter_id, x, y, 0); - } - return nullptr; -} - -void advance_screen_particles(ParticleEmitterInfo* particle_emitter) -{ - static size_t offset = get_address("advance_screen_particles"); - - if (offset != 0) - { - typedef void advance_particles_func(ParticleEmitterInfo*); - static advance_particles_func* apf = (advance_particles_func*)(offset); - apf(particle_emitter); - } -} - -void render_screen_particles(ParticleEmitterInfo* particle_emitter) -{ - static size_t offset = get_address("render_screen_particles"); - - if (offset != 0) - { - typedef void render_particles_func(ParticleEmitterInfo*, size_t, size_t, size_t); - static render_particles_func* rpf = (render_particles_func*)(offset); - rpf(particle_emitter, 0, 0, 0); - } -} - -void extinguish_particles(ParticleEmitterInfo* particle_emitter) -{ - // removing from state only applies to world emitters, but it just won't find the screen one in the vector, so no big deal - auto state = get_state_ptr(); - std::erase(*state->particle_emitters, particle_emitter); - - using generic_free_func = void(void*); - static generic_free_func* generic_free = (generic_free_func*)get_address("generic_free"); - - if (particle_emitter != nullptr) - { - generic_free(particle_emitter->emitted_particles.memory); - generic_free(particle_emitter->emitted_particles_back_layer.memory); - generic_free(particle_emitter); - } -} - -Illumination* create_illumination_internal(Color color, float size, float x, float y, int32_t uid) -{ - static size_t offset = get_address("generate_illumination"); - - if (offset != 0) - { - auto state = get_state_ptr(); - - float position[] = {x, y}; - - typedef Illumination* create_illumination_func(std::vector*, float*, Color*, uint32_t r9, float size, uint8_t flags, uint32_t uid, uint8_t); - static create_illumination_func* cif = (create_illumination_func*)(offset); - auto emitted_light = cif(state->lightsources, position, &color, 0x2, size, 32, uid, 0x0); - - // turn on Enabled flag - emitted_light->flags = emitted_light->flags | (1U << (25 - 1)); - - return emitted_light; - } - return nullptr; -} - -Illumination* create_illumination(Color color, float size, float x, float y) -{ - return create_illumination_internal(color, size, x, y, -1); -} - -Illumination* create_illumination(Color color, float size, uint32_t uid) -{ - auto entity = get_entity_ptr(uid); - if (entity != nullptr) - { - return create_illumination_internal(color, size, entity->abs_x, entity->abs_y, uid); - } - return nullptr; -} - -void refresh_illumination(Illumination* illumination) -{ - static uint32_t* offset = 0; - if (offset == 0) - { - size_t** heap_offset = (size_t**)get_address("refresh_illumination_heap_offset"); - auto illumination_counter = OnHeapPointer(**heap_offset); - offset = illumination_counter.decode(); - } - illumination->timer = *offset; + return false; } void set_journal_enabled(bool b) @@ -1411,14 +1295,14 @@ void add_item_to_shop(int32_t item_uid, int32_t shop_owner_uid) { if (owner->type->id == it) // TODO: check what happens if it's not room owner/shopkeeper { - auto state = State::get(); + auto state = State::get().ptr(); item->flags = setflag(item->flags, 23); // shop item item->flags = setflag(item->flags, 20); // Enable button prompt (flag is problably: show dialogs and other fx) - state.layer_local(item->layer)->spawn_entity_over(to_id("ENT_TYPE_FX_SALEICON"), item, 0, 0); - state.layer_local(item->layer)->spawn_entity_over(to_id("ENT_TYPE_FX_SALEDIALOG_CONTAINER"), item, 0, 0.5); + state->layers[item->layer]->spawn_entity_over(to_id("ENT_TYPE_FX_SALEICON"), item, 0, 0); + state->layers[item->layer]->spawn_entity_over(to_id("ENT_TYPE_FX_SALEDIALOG_CONTAINER"), item, 0, 0.5); ItemOwnerDetails iod{shop_owner_uid, owner->type->id}; - state.ptr()->room_owners.owned_items.insert({item->uid, iod}); + state->room_owners.owned_items.insert({item->uid, iod}); return; } } @@ -1510,11 +1394,10 @@ void game_log(std::string message) game_log_fun(log_stream, message.c_str(), nullptr, LogLevel::Info); } -void call_death_screen() +void load_death_screen() { - using DeathScreen = void(); - static auto death_screen = (DeathScreen*)get_address("death_screen"); - death_screen(); + auto state = State::get().ptr(); + state->screen_death->init(); } void save_progress() @@ -1938,18 +1821,34 @@ void create_level() create_layer(1); } -void set_death_enabled(bool enable) +void set_level_logic_enabled(bool enable) { - static size_t offset = 0; - if (offset == 0) - { - offset = get_address("dead_players"); - } + auto state = State::get().ptr(); + static size_t offset = get_virtual_function_address(state->screen_level, 1); + if (offset != 0) { if (!enable) - write_mem_recoverable("death_disable", offset, "\xC3\x90"sv, true); + write_mem_recoverable("set_level_logic_enabled", offset, "\xC3\x90"sv, true); else - recover_mem("death_disable"); + recover_mem("set_level_logic_enabled"); + } +} + +void set_camera_layer_control_enabled(bool enable) +{ + static auto offset = get_address("camera_layer_controll"); + static auto offset2 = get_address("player_behavior_layer_switch"); + if (!offset || !offset2) + return; + + if (enable) + { + recover_mem("set_camera_layer_control"); + } + else + { + write_mem_recoverable("set_camera_layer_control", offset, get_nop(7), true); + write_mem_recoverable("set_camera_layer_control", offset2, get_nop(18), true); } } diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 3b366f459..6428577d3 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -72,15 +72,6 @@ void force_olmec_phase_0(bool b); void set_ghost_spawn_times(uint32_t normal = 10800, uint32_t cursed = 9000); void set_time_ghost_enabled(bool b); void set_time_jelly_enabled(bool b); -ParticleEmitterInfo* generate_world_particles(uint32_t particle_emitter_id, uint32_t uid); -ParticleEmitterInfo* generate_screen_particles(uint32_t particle_emitter_id, float x, float y); -void advance_screen_particles(ParticleEmitterInfo* particle_emitter); -void render_screen_particles(ParticleEmitterInfo* particle_emitter); -void extinguish_particles(ParticleEmitterInfo* particle_emitter); -Illumination* create_illumination_internal(Color color, float size, float x, float y, int32_t uid); -Illumination* create_illumination(Color color, float size, float x, float y); -Illumination* create_illumination(Color color, float size, uint32_t uid); -void refresh_illumination(Illumination* illumination); void set_journal_enabled(bool b); void set_camp_camera_bounds_enabled(bool b); void set_explosion_mask(int32_t mask); @@ -114,7 +105,7 @@ void update_liquid_collision_at(float x, float y, bool add); bool disable_floor_embeds(bool disable); void set_cursepot_ghost_enabled(bool enable); void game_log(std::string message); -void call_death_screen(); +void load_death_screen(); void save_progress(); void set_level_string(std::u16string_view text); void set_ending_unlock(ENT_TYPE type); @@ -138,4 +129,5 @@ void destroy_layer(uint8_t layer); void destroy_level(); void create_layer(uint8_t layer); void create_level(); -void set_death_enabled(bool enable); +void set_level_logic_enabled(bool enable); +void set_camera_layer_control_enabled(bool enable); diff --git a/src/game_api/screen.hpp b/src/game_api/screen.hpp index 35ee2adfd..bcd32ede1 100644 --- a/src/game_api/screen.hpp +++ b/src/game_api/screen.hpp @@ -24,7 +24,7 @@ class Screen uint32_t unknown_zero; virtual void init() = 0; - virtual void handle_player() = 0; // for normal level: death, camera zoom, camera bounds, some save data stuff + virtual void handle_player() = 0; // for normal level: death, camera zoom (level/shop), camera bounds, some save data stuff virtual ~Screen() = 0; virtual void render() = 0; // mostly used by the non gameplay screens to draw textures and text @@ -484,6 +484,8 @@ class ScreenLevel : public Screen // ID: 12 { public: uint8_t buttons; + /// Delay after player death to open the death screen + int8_t time_till_death_screen; }; class ScreenTransition : public Screen // ID: 13 @@ -1072,7 +1074,7 @@ struct JournalUI uint8_t unknown1; uint16_t unknown2; /// Stores pages loaded into memeory. It's not cleared after the journal is closed or when you go back to the main (menu) page. - /// Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_POST_DRAW_DEPTH](#ON-RENDER_PRE_JOURNAL_PAGE)) + /// Use `:get_type()` to chcek page type and cast it correctly (see ON.[RENDER_PRE_JOURNAL_PAGE](#ON-RENDER_PRE_JOURNAL_PAGE)) custom_vector pages; custom_vector pages_tmp; // pages are constructed in the show_journal function and put here, later transfered to the pages vector uint32_t current_page; diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index 0c12c0439..c0f89c1fb 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -36,8 +36,10 @@ #include "entities_items.hpp" // for Container, Player... #include "entity.hpp" // for get_entity_ptr #include "entity_lookup.hpp" // +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager #include "handle_lua_function.hpp" // for handle_function +#include "illumination.hpp" // #include "items.hpp" // for Inventory #include "layer.hpp" // for g_level_max_x #include "lua_backend.hpp" // for LuaBackend, ON @@ -96,7 +98,7 @@ #include "usertypes/texture_lua.hpp" // for register_usertypes #include "usertypes/vanilla_render_lua.hpp" // for VanillaRenderContext #include "usertypes/vtables_lua.hpp" // for register_usertypes -#include "virtual_table.hpp" +#include "virtual_table.hpp" // struct Illumination; @@ -671,26 +673,26 @@ end { return read_prng(); }; using Toast = void(const char16_t*); - using Say = void(size_t, Entity*, const char16_t*, int, bool); + using Say = void(HudData*, Entity*, const char16_t*, int, bool); /// Show a message that looks like a level feeling. lua["toast"] = [](std::u16string message) { - static Toast* toast_fun = (Toast*)get_address("toast"); + static auto toast_fun = (Toast*)get_address("toast"); toast_fun(message.c_str()); }; /// Show a message coming from an entity lua["say"] = [](uint32_t entity_uid, std::u16string message, int sound_type, bool top) { static auto say = (Say*)get_address("speech_bubble_fun"); - static const auto say_context = get_address("say_context"); + const auto hud = get_hud(); auto entity = get_entity_ptr(entity_uid); if (entity == nullptr) return; - say(say_context, entity, message.c_str(), sound_type, top); + say(hud, entity, message.c_str(), sound_type, top); }; /// Add an integer option that the user can change in the UI. Read with `options.name`, `value` is the default. Keep in mind these are just soft /// limits, you can override them in the UI with double click. @@ -1107,7 +1109,7 @@ end /// Set the `more_flags` field from entity by uid lua["set_entity_flags2"] = set_entity_flags2; /// Deprecated - /// As the name is misleading. use entity `move_state` field instead + /// As the name is misleading. use Movable.`move_state` field instead lua["get_entity_ai_state"] = get_entity_ai_state; /// Get `state.level_flags` lua["get_level_flags"] = get_level_flags; @@ -1117,7 +1119,10 @@ end lua["get_entity_type"] = get_entity_type; /// Get the current set zoom level lua["get_zoom_level"] = []() -> float - { return State::get_zoom_level(); }; + { + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); + }; /// Get the game coordinates at the screen position (`x`, `y`) lua["game_position"] = [](float x, float y) -> std::pair { return State::click_position(x, y); }; @@ -1798,11 +1803,12 @@ end lua["change_poison_timer"] = change_poison_timer; auto create_illumination = sol::overload( - static_cast(::create_illumination), - static_cast(::create_illumination)); - /// Creates a new Illumination. Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example + static_cast(::create_illumination), + static_cast(::create_illumination), + static_cast(::create_illumination)); + /// Creates a new Illumination. Don't forget to continuously call [refresh_illumination](#refresh_illumination), otherwise your light emitter fades out! Check out the [illumination.lua](https://github.com/spelunky-fyi/overlunky/blob/main/examples/illumination.lua) script for an example. lua["create_illumination"] = create_illumination; - /// Refreshes an Illumination, keeps it from fading out + /// Refreshes an Illumination, keeps it from fading out (updates the timer, keeping it in sync with the game render) lua["refresh_illumination"] = refresh_illumination; /// Removes all liquid that is about to go out of bounds, which crashes the game. @@ -1910,20 +1916,20 @@ end /// Get the rva for a pattern name, used for debugging. lua["get_rva"] = [](std::string_view address_name) -> std::string { - return fmt::format("{:x}", get_address(address_name) - Memory::get().at_exe(0)); + return fmt::format("{:X}", get_address(address_name) - Memory::get().at_exe(0)); }; /// Get the rva for a vtable offset and index, used for debugging. lua["get_virtual_rva"] = [](VTABLE_OFFSET offset, uint32_t index) -> std::string { - return fmt::format("{:x}", get_virtual_function_address(offset, index)); + return fmt::format("{:X}", get_virtual_function_address(offset, index)); }; /// Log to spelunky.log lua["log_print"] = game_log; /// Immediately ends the run with the death screen, also calls the [save_progress](#save_progress) - lua["load_death_screen"] = call_death_screen; + lua["load_death_screen"] = load_death_screen; /// Saves the game to savegame.sav, unless game saves are blocked in the settings. Also runs the ON.SAVE callback. Fails and returns false, if you're trying to save too often (2s). lua["save_progress"] = []() -> bool @@ -2155,7 +2161,7 @@ end lua["create_layer"] = create_layer; /// Setting to false disables all player logic in SCREEN.LEVEL, mainly the death screen from popping up if all players are dead or missing, but also shop camera zoom and some other small things. - lua["set_level_logic_enabled"] = set_death_enabled; + lua["set_level_logic_enabled"] = set_level_logic_enabled; /// Converts INPUTS to (x, y, BUTTON) lua["inputs_to_buttons"] = [](INPUTS inputs) -> std::tuple @@ -2196,6 +2202,11 @@ end backend->infinite_loop_detection = enable; }; + /// This disables the `state.camera_layer` to be forced to the `(leader player).layer` and setting of the `state.layer_transition_timer` & `state.transition_to_layer` when player enters layer door. + /// Letting you control those manually. + /// Look at the example on how to mimic game layer switching behavior + lua["set_camera_layer_control_enabled"] = set_camera_layer_control_enabled; + lua.create_named_table("INPUTS", "NONE", 0, "JUMP", 1, "WHIP", 2, "BOMB", 4, "ROPE", 8, "RUN", 16, "DOOR", 32, "MENU", 64, "JOURNAL", 128, "LEFT", 256, "RIGHT", 512, "UP", 1024, "DOWN", 2048); lua.create_named_table( diff --git a/src/game_api/script/usertypes/entities_activefloors_lua.cpp b/src/game_api/script/usertypes/entities_activefloors_lua.cpp index 66f24e2a4..e7cad6460 100644 --- a/src/game_api/script/usertypes/entities_activefloors_lua.cpp +++ b/src/game_api/script/usertypes/entities_activefloors_lua.cpp @@ -10,9 +10,9 @@ #include "entities_activefloors.hpp" // for Olmec, ClamBase, Crushtrap, Ele... #include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep #include "particles.hpp" // IWYU pragma: keep -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep class Movable; diff --git a/src/game_api/script/usertypes/entities_backgrounds_lua.cpp b/src/game_api/script/usertypes/entities_backgrounds_lua.cpp index aed70bc2c..c6e060874 100644 --- a/src/game_api/script/usertypes/entities_backgrounds_lua.cpp +++ b/src/game_api/script/usertypes/entities_backgrounds_lua.cpp @@ -10,7 +10,7 @@ #include "entities_backgrounds.hpp" // for BGEggshipRoom, BGShootingStar #include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep namespace NEntitiesBG { diff --git a/src/game_api/script/usertypes/entities_chars_lua.cpp b/src/game_api/script/usertypes/entities_chars_lua.cpp index b6157bd82..d708773cb 100644 --- a/src/game_api/script/usertypes/entities_chars_lua.cpp +++ b/src/game_api/script/usertypes/entities_chars_lua.cpp @@ -11,6 +11,7 @@ #include "entities_chars.hpp" // for Ai, Player, PowerupCapable, get_charac... #include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep #include "items.hpp" // for Inventory, Inventory::acquired_powerups #include "state_structs.hpp" // IWYU pragma: keep diff --git a/src/game_api/script/usertypes/entities_decorations_lua.cpp b/src/game_api/script/usertypes/entities_decorations_lua.cpp index 0c63a2470..7f9216a75 100644 --- a/src/game_api/script/usertypes/entities_decorations_lua.cpp +++ b/src/game_api/script/usertypes/entities_decorations_lua.cpp @@ -10,7 +10,7 @@ #include "entities_decorations.hpp" // for PalaceSign, CrossBeam, DecoRegen... #include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep namespace NEntitiesDecorations { diff --git a/src/game_api/script/usertypes/entities_floors_lua.cpp b/src/game_api/script/usertypes/entities_floors_lua.cpp index b7da6cdcc..1ac1b6ddc 100644 --- a/src/game_api/script/usertypes/entities_floors_lua.cpp +++ b/src/game_api/script/usertypes/entities_floors_lua.cpp @@ -11,8 +11,8 @@ #include "entities_floors.hpp" // for MotherStatue, Floor, ExitDoor, Door #include "entity.hpp" // for Entity -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep namespace NEntitiesFloors { diff --git a/src/game_api/script/usertypes/entities_fx_lua.cpp b/src/game_api/script/usertypes/entities_fx_lua.cpp index 352cba841..fd047eb9b 100644 --- a/src/game_api/script/usertypes/entities_fx_lua.cpp +++ b/src/game_api/script/usertypes/entities_fx_lua.cpp @@ -8,9 +8,9 @@ #include // for move, declval #include // for min, max -#include "entities_fx.hpp" // for FxSaleContainer, Button, FxAnkhRotatingSpark -#include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "entities_fx.hpp" // for FxSaleContainer, Button, FxAnkhRotatingSpark +#include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep class Movable; diff --git a/src/game_api/script/usertypes/entities_items_lua.cpp b/src/game_api/script/usertypes/entities_items_lua.cpp index 31c3337ce..1748576f1 100644 --- a/src/game_api/script/usertypes/entities_items_lua.cpp +++ b/src/game_api/script/usertypes/entities_items_lua.cpp @@ -11,9 +11,10 @@ #include "entities_items.hpp" // for Spark, Boombox, AnkhPowerup, Arrow #include "entity.hpp" // for Entity +#include "illumination.hpp" // IWYU pragma: keep #include "items.hpp" // IWYU pragma: keep #include "particles.hpp" // IWYU pragma: keep -#include "sound_manager.hpp" // +#include "sound_manager.hpp" // IWYU pragma: keep #include "state_structs.hpp" // IWYU pragma: keep class Movable; diff --git a/src/game_api/script/usertypes/entities_liquids_lua.cpp b/src/game_api/script/usertypes/entities_liquids_lua.cpp index 98125a29c..2d8d21b95 100644 --- a/src/game_api/script/usertypes/entities_liquids_lua.cpp +++ b/src/game_api/script/usertypes/entities_liquids_lua.cpp @@ -10,7 +10,7 @@ #include "entities_liquids.hpp" // for Liquid, Lava, Lava::emitted_light #include "entity.hpp" // for Entity -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep namespace NEntitiesLiquids { diff --git a/src/game_api/script/usertypes/entities_logical_lua.cpp b/src/game_api/script/usertypes/entities_logical_lua.cpp index 6ac97572c..2047a75fb 100644 --- a/src/game_api/script/usertypes/entities_logical_lua.cpp +++ b/src/game_api/script/usertypes/entities_logical_lua.cpp @@ -10,8 +10,8 @@ #include "entities_logical.hpp" // for Portal, DMSpawning, LogicalDoor, Our... #include "entity.hpp" // for Entity -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep namespace NEntitiesLogical { diff --git a/src/game_api/script/usertypes/entities_monsters_lua.cpp b/src/game_api/script/usertypes/entities_monsters_lua.cpp index d7215e0ca..1304b8f52 100644 --- a/src/game_api/script/usertypes/entities_monsters_lua.cpp +++ b/src/game_api/script/usertypes/entities_monsters_lua.cpp @@ -10,8 +10,8 @@ #include "entities_monsters.hpp" // for CritterSlime, Hundun, Tiamat, Critt... #include "entity.hpp" // for Entity -#include "sound_manager.hpp" // -#include "state_structs.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep +#include "sound_manager.hpp" // IWYU pragma: keep class Movable; class PowerupCapable; diff --git a/src/game_api/script/usertypes/entity_lua.cpp b/src/game_api/script/usertypes/entity_lua.cpp index a8aaec46e..5c492c951 100644 --- a/src/game_api/script/usertypes/entity_lua.cpp +++ b/src/game_api/script/usertypes/entity_lua.cpp @@ -53,6 +53,7 @@ void register_usertypes(sol::state& lua) color_type["set_rgba"] = &Color::set_rgba; color_type["get_ucolor"] = &Color::get_ucolor; color_type["set_ucolor"] = &Color::set_ucolor; + color_type["set"] = &Color::set; /// Used in EntityDB lua.new_usertype( diff --git a/src/game_api/script/usertypes/hitbox_lua.cpp b/src/game_api/script/usertypes/hitbox_lua.cpp index 05201a5b0..a581bb568 100644 --- a/src/game_api/script/usertypes/hitbox_lua.cpp +++ b/src/game_api/script/usertypes/hitbox_lua.cpp @@ -67,6 +67,8 @@ void register_usertypes(sol::state& lua) &Vec2::rotate, "distance_to", &Vec2::distance_to, + "set", + &Vec2::set, "split", // &Vec2::split); // for the autodoc &Vec2::operator std::pair); @@ -108,6 +110,8 @@ void register_usertypes(sol::state& lua) &AABB::height, "is_point_inside", is_point_inside, + "set", + &AABB::set, "split", // &Vec2::split); // for the autodoc &AABB::operator std::tuple); @@ -116,8 +120,8 @@ void register_usertypes(sol::state& lua) static_cast(&Triangle::offset), static_cast(&Triangle::offset)); auto is_point_inside_triangle = sol::overload( - static_cast(&Triangle::is_point_inside), - static_cast(&Triangle::is_point_inside), + static_cast(&Triangle::is_point_inside), + static_cast(&Triangle::is_point_inside), static_cast(&Triangle::is_point_inside), static_cast(&Triangle::is_point_inside)); @@ -144,19 +148,21 @@ void register_usertypes(sol::state& lua) &Triangle::area, "is_point_inside", is_point_inside_triangle, + "set", + &Triangle::set, "split", // &Triangle::split); // for the autodoc &Triangle::operator std::tuple); auto is_point_inside_quad = sol::overload( - static_cast(&Quad::is_point_inside), - static_cast(&Quad::is_point_inside), + static_cast(&Quad::is_point_inside), + static_cast(&Quad::is_point_inside), static_cast(&Quad::is_point_inside), static_cast(&Quad::is_point_inside)); lua.new_usertype( "Quad", - sol::constructors{}, + sol::constructors{}, "bottom_left_x", &Quad::bottom_left_x, "bottom_left_y", @@ -185,13 +191,15 @@ void register_usertypes(sol::state& lua) &Quad::flip_vertically, "is_point_inside", is_point_inside_quad, + "set", + &Quad::set, "split", // &Quad::split); // for the autodoc &Quad::operator std::tuple); auto two_lines_angle = sol::overload( - static_cast(::two_lines_angle), - static_cast(::two_lines_angle)); + static_cast(::two_lines_angle), + static_cast(::two_lines_angle)); lua["intersection"] = intersection; lua["two_lines_angle"] = two_lines_angle; } diff --git a/src/game_api/script/usertypes/particles_lua.cpp b/src/game_api/script/usertypes/particles_lua.cpp index b727a8351..4c426b1dd 100644 --- a/src/game_api/script/usertypes/particles_lua.cpp +++ b/src/game_api/script/usertypes/particles_lua.cpp @@ -44,7 +44,7 @@ void register_usertypes(sol::state& lua) lua["generate_particles"] = generate_world_particles; /// Used in ParticleDB, [get_particle_type](#get_particle_type) - auto particledb_type = lua.new_usertype("ParticleDB"); + auto particledb_type = lua.new_usertype("ParticleDB", sol::constructors()); particledb_type["id"] = &ParticleDB::id; particledb_type["spawn_count_min"] = &ParticleDB::spawn_count_min; particledb_type["spawn_count"] = &ParticleDB::spawn_count; @@ -82,6 +82,8 @@ void register_usertypes(sol::state& lua) "ParticleEmitterInfo", "particle_type", &ParticleEmitterInfo::particle_type, + "particle_type2", + &ParticleEmitterInfo::particle_type2, "particle_count", sol::property([](ParticleEmitterInfo& e) -> uint32_t { return e.emitted_particles.particle_count; }), @@ -98,6 +100,10 @@ void register_usertypes(sol::state& lua) &ParticleEmitterInfo::offset_x, "offset_y", &ParticleEmitterInfo::offset_y, + "layer", + &ParticleEmitterInfo::layer, + "draw_depth", + &ParticleEmitterInfo::draw_depth, "emitted_particles", &ParticleEmitterInfo::emitted_particles, "emitted_particles_back_layer", diff --git a/src/game_api/script/usertypes/screen_lua.cpp b/src/game_api/script/usertypes/screen_lua.cpp index 42cb1b41b..29ec72042 100644 --- a/src/game_api/script/usertypes/screen_lua.cpp +++ b/src/game_api/script/usertypes/screen_lua.cpp @@ -347,6 +347,8 @@ void register_usertypes(sol::state& lua) "ScreenLevel", "buttons", &ScreenLevel::buttons, + "time_till_death_screen", + &ScreenLevel::time_till_death_screen, sol::base_classes, sol::bases()); diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index 87751a4b0..d0872d672 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -14,6 +14,7 @@ #include "entities_chars.hpp" // IWYU pragma: keep #include "entity.hpp" // IWYU pragma: keep +#include "illumination.hpp" // IWYU pragma: keep #include "items.hpp" // for Items, SelectPlayerSlot, Items::is... #include "level_api.hpp" // IWYU pragma: keep #include "online.hpp" // for OnlinePlayer, OnlineLobby, Online @@ -347,7 +348,8 @@ void register_usertypes(sol::state& lua) statememory_type["correct_ushabti"] = &StateMemory::correct_ushabti; statememory_type["items"] = &StateMemory::items; statememory_type["camera_layer"] = &StateMemory::camera_layer; - statememory_type["layer_transition_timer"] = &StateMemory::layer_transition_effect_timer; + statememory_type["layer_transition_timer"] = &StateMemory::layer_transition_timer; + statememory_type["transition_to_layer"] = &StateMemory::transition_to_layer; statememory_type["screen_team_select"] = &StateMemory::screen_team_select; statememory_type["screen_character_select"] = &StateMemory::screen_character_select; statememory_type["screen_transition"] = &StateMemory::screen_transition; @@ -410,7 +412,9 @@ void register_usertypes(sol::state& lua) "blue", &LightParams::blue, "size", - &LightParams::size); + &LightParams::size, + "as_color", + &LightParams::as_color); /// Generic obcject for lights in the game, you can make your own with [create_illumination](#create_illumination)
/// Used in StateMemory, Player, PlayerGhost, BurningRopeEffect ... @@ -428,11 +432,25 @@ void register_usertypes(sol::state& lua) illumination_type["offset_y"] = &Illumination::offset_y; illumination_type["distortion"] = &Illumination::distortion; illumination_type["entity_uid"] = &Illumination::entity_uid; + illumination_type["timer"] = &Illumination::timer; illumination_type["flags"] = &Illumination::flags; illumination_type["type_flags"] = &Illumination::type_flags; illumination_type["enabled"] = &Illumination::enabled; illumination_type["layer"] = &Illumination::layer; + lua.create_named_table("LIGHT_TYPE", "NONE", LIGHT_TYPE::NONE, "FOLLOW_CAMERA", LIGHT_TYPE::FOLLOW_CAMERA, "FOLLOW_ENTITY", LIGHT_TYPE::FOLLOW_ENTITY, "ROOM_LIGHT", LIGHT_TYPE::ROOM_LIGHT); + + /* LIGHT_TYPE + // NONE + // Normal static light, position can be edited to move it around + // FOLLOW_CAMERA + // Position is updated to the camera position, can be moved around via offset + // FOLLOW_ENTITY + // Position is updated to the entity position (from the uid field), if the uid is not found it will behave as LIGHT_TYPE.NONE, can be moved around via offset + // ROOM_LIGHT + // Rectangle, full brightness always uses light1, disabling light1 does nothing + */ + auto camera_type = lua.new_usertype("Camera"); camera_type["bounds_left"] = &Camera::bounds_left; camera_type["bounds_right"] = &Camera::bounds_right; diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index a566dcbd1..102ac3c3f 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -488,6 +488,7 @@ std::unordered_map g_address_rules{ }, { "game_free"sv, + // it's function that decides to use game_free or custom_free by the address PatternCommandBuffer{} .find_inst("\x48\x83\x7e\x18\x00"sv) .offset(-0x10) @@ -530,6 +531,7 @@ std::unordered_map g_address_rules{ }, { "state_location"sv, + // actually it's state offset, at the time of writing this comment it's 4A0, found ... almost everywhere PatternCommandBuffer{} .find_inst("\x49\x0F\x44\xC0"sv) .find_next_inst("\x49\x0F\x44\xC0"sv) @@ -571,25 +573,25 @@ std::unordered_map g_address_rules{ .decode_pc(4) .at_exe(), }, - { - "render_api_callback"sv, - // Break at startup on SteamAPI_RegisterCallback, it gets called twice, second time - // to hook the Steam overlay, at the beginning of that function is the pointer we need - PatternCommandBuffer{} - .find_inst("\x70\x08\x00\x00\xFE\xFF\xFF\xFF\x48\x8B\x05"sv) - .offset(0x8) - .decode_pc() - .at_exe(), - }, - { - // Find reference to SetWindowPos or SetWindowLongA, below one of the references you should see a few instructions like: - // mov rcx,qword ptr ds:[rsi+80FD0] - "render_api_offset"sv, - PatternCommandBuffer{} - .find_inst("\xBA\xF0\xFF\xFF\xFF\x41\xB8\x00\x00\x00\x90"sv) - .offset(0x11) - .decode_imm(), - }, + //{ + // "render_api_callback"sv, + // // Break at startup on SteamAPI_RegisterCallback, it gets called twice, second time + // // to hook the Steam overlay, at the beginning of that function is the pointer we need + // PatternCommandBuffer{} + // .find_inst("\x70\x08\x00\x00\xFE\xFF\xFF\xFF\x48\x8B\x05"sv) + // .offset(0x8) + // .decode_pc() + // .at_exe(), + //}, + //{ + // // Find reference to SetWindowPos or SetWindowLongA, below one of the references you should see a few instructions like: + // // mov rcx,qword ptr ds:[rsi+80FD0] + // "render_api_offset"sv, + // PatternCommandBuffer{} + // .find_inst("\xBA\xF0\xFF\xFF\xFF\x41\xB8\x00\x00\x00\x90"sv) + // .offset(0x11) + // .decode_imm(), + //}, { // in load_item it's written to RCX and then calls spawn_entity "entity_factory"sv, @@ -1043,30 +1045,30 @@ std::unordered_map g_address_rules{ .decode_pc(2) .at_exe(), }, - { - "zoom_level"sv, - // Go stand in a level next to a shop. In Cheat Engine, search for float 13.5 - // Go into the shop, search for 12.5, put a write bp on that address. - // In 1.23.3 the default shop and level zoom levels aren't hardcoded in the exe, they are - // in variables (loaded into xmm6) - // Note that xmm6 (the new zoom level) gets written at a huge offset of rax. Rax is the - // return value of the call just above, so look in that function at the bottom. There will - // be a hardcoded value loaded in rax. At offset 0x10 in rax is another pointer that is the - // base for the big offset. - PatternCommandBuffer{} - .find_inst("\x48\x8B\x05****\x48\x81\xC4\xF8\x08\x00\x00"sv) - .decode_pc() - .at_exe(), - }, - { - "zoom_level_offset"sv, - // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. - // That instruction contains the offset, the memory is: {current_zoom, target_zoom} and both offset will be present - // current solution uses the target_zoom offset - PatternCommandBuffer{} - .find_inst("\xF3\x0F\x11\xB0****\x49"sv) - .decode_imm(4), - }, + //{ + // "zoom_level"sv, + // // Go stand in a level next to a shop. In Cheat Engine, search for float 13.5 + // // Go into the shop, search for 12.5, put a write bp on that address. + // // In 1.23.3 the default shop and level zoom levels aren't hardcoded in the exe, they are + // // in variables (loaded into xmm6) + // // Note that xmm6 (the new zoom level) gets written at a huge offset of rax. Rax is the + // // return value of the call just above, so look in that function at the bottom. There will + // // be a hardcoded value loaded in rax. At offset 0x10 in rax is another pointer that is the + // // base for the big offset. + // PatternCommandBuffer{} + // .find_inst("\x48\x8B\x05****\x48\x81\xC4\xF8\x08\x00\x00"sv) + // .decode_pc() + // .at_exe(), + //}, + //{ + // "zoom_level_offset"sv, + // // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. + // // That instruction contains the offset, the memory is: {current_zoom, target_zoom} and both offset will be present + // // current solution uses the target_zoom offset + // PatternCommandBuffer{} + // .find_inst("\xF3\x0F\x11\xB0****\x49"sv) + // .decode_imm(4), + //}, { "default_zoom_level"sv, // Follow the same logic as in `zoom_level` to get to the point where the zoom level is written. @@ -1114,6 +1116,7 @@ std::unordered_map g_address_rules{ "coord_inside_active_shop_room"sv, // Same pattern as default_zoom_level_shop, check the condition before the jump that decides whether to activate // the shop zoom level or regular zoom level + // Found at second to last function inside screen level -> handle players (second) virtual function PatternCommandBuffer{} .find_inst("\xF3\x0F\x11\xB0****\x49"sv) .offset(-0x24) @@ -1123,22 +1126,13 @@ std::unordered_map g_address_rules{ { "coord_inside_shop_zone"sv, // Can be found in same function as default_zoom_level_shop, check the condition higher up + // Also found in screen level -> handle players (second) virtual function but more like in the middle of it PatternCommandBuffer{} .find_inst("\x40\x8A\xBB\xA0\x00\x00\x00\x89\xFA\xE8"sv) .offset(0x9) .decode_call() .at_exe(), }, - { - "coord_inside_shop_zone_rcx"sv, - // See coord_inside_shop_zone, a little higher up rcx gets set to an on heap pointer - PatternCommandBuffer{} - .find_inst("\x0F\x84****\x48\x8B\x05****\x4A\x8D\x0C\x08"sv) - .find_next_inst("\x0F\x84****\x48\x8B\x05****\x4A\x8D\x0C\x08"sv) - .offset(0x6) - .decode_pc() - .at_exe(), - }, { "enforce_camp_camera_bounds"sv, // Go into basecamp, put a write bp on state.camera.bounds.top @@ -1264,6 +1258,7 @@ std::unordered_map g_address_rules{ "show_journal"sv, // aka render_journal / open journal chapter // Break on GameManager.journal_ui.state, open the journal + // Or go to state->death screen, to first virtul function, the second call in that virtual is the function PatternCommandBuffer{} .find_inst("88 5F 04 80 FB 0B 0F"_gh) .at_exe() @@ -1540,15 +1535,6 @@ std::unordered_map g_address_rules{ .at_exe() .function_start(), }, - { - "say_context"sv, - // Find the pattern for `say`, go one up higher in the callstack and look what writes to rcx - PatternCommandBuffer{} - .find_after_inst("\xC6\x44\x24\x20\x01\x48\x8D\x0D"sv) - .offset(-0x3) - .decode_pc() - .at_exe(), - }, { "force_dark_level"sv, // Put a write bp on State.level_flags (3rd byte, containing dark level flag) @@ -1878,16 +1864,7 @@ std::unordered_map g_address_rules{ .function_start(), }, { - // Set write bp on write_to_file, take a death in game, return from write_to_file, then from the next function as well - // you should be now in the death_screen function, the first call is also the save_progress function - "death_screen"sv, - PatternCommandBuffer{} - .find_inst("4D 0F 44 C1 49 8B 88 F0 12 00 00"_gh) - .at_exe() - .function_start(), - }, - { - // see death_screen + // go to state->death screen, to first virtual, this is the first call in that virtual "save_progress"sv, PatternCommandBuffer{} .find_inst("48 8B 90 F0 12 00 00 8B 5A 28"_gh) @@ -2069,45 +2046,30 @@ std::unordered_map g_address_rules{ //.from_exe_base(0x228b58f0), }, { - "dead_players"sv, - // I guess it writes 14 to screen_next before the death screen pops up. Apparently it's a SCREEN_LEVEL virtual too. + "camera_layer_controll"sv, + // overwrites state.camera_layer every frame PatternCommandBuffer{} - .find_inst("4c 8b b8 e8 12 00 00 48 8b 80 f0 12 00 00"_gh) - .at_exe() - .function_start(), - //.from_exe_base(0x22c061d0), - }, - { - "spawn_transition"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION - PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_DWELLING, VIRT_FUNC::THEME_SPAWN_TRANSITION) - .at_exe(), - //.from_exe_base(0x22afe5c0), - }, - { - "spawn_transition_cosmic"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION - PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_COSMICOCEAN, VIRT_FUNC::THEME_SPAWN_TRANSITION) + .get_address("state_refresh"sv) + .find_after_inst("8A 80 A0 00 00 00"_gh) .at_exe(), - //.from_exe_base(0x22b373b0), }, { - "spawn_transition_duat"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION + "player_behavior_layer_switch"sv, + // function in player behavior (index 7), we need instruction that sets state.layer_transition_timer = 0x24 and state.transition_to_layer = (dest layer) PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_CITY_OF_GOLD, VIRT_FUNC::THEME_SPAWN_TRANSITION) + .find_after_inst("41 80 FC 01 0F 95 C1"_gh) + .find_inst("\xE8"sv) + .offset(0x5) .at_exe(), - //.from_exe_base(0x22b34940), }, { - "spawn_transition_olmecship"sv, - // These functions are hooked separately cause hooking the vtable just didn't work right for POST_LEVEL_GENERATION + "get_game_api"sv, + // can be found together with get_feat function PatternCommandBuffer{} - .get_virtual_function_address(VTABLE_OFFSET::THEME_BASECAMP, VIRT_FUNC::THEME_SPAWN_TRANSITION) + .find_after_inst("49 89 CE 4C 8B 79 08"_gh) + .find_inst("\xE8"sv) + .decode_call() .at_exe(), - //.from_exe_base(0x22b2d350), }, }; std::unordered_map g_cached_addresses; @@ -2120,6 +2082,13 @@ void preload_addresses() { if (auto address = rule(mem, exe, address_name)) { + for (auto& [k, v] : g_cached_addresses) + { + if (v == address.value() && k != address_name) + { + DEBUG("Two patterns refer to the same address: {} & {}", k, address_name); + } + } g_cached_addresses[address_name] = address.value(); } } diff --git a/src/game_api/settings_api.cpp b/src/game_api/settings_api.cpp index 1a1eba15c..604c70ddf 100644 --- a/src/game_api/settings_api.cpp +++ b/src/game_api/settings_api.cpp @@ -8,8 +8,8 @@ #include // for hash, move, conditional_t #include // for unordered_set, _Uset_traits<>::allocator_type -#include "render_api.hpp" // for RenderAPI -#include "search.hpp" // for get_address +#include "game_api.hpp" // +#include "search.hpp" // for get_address union SettingValue { @@ -102,8 +102,11 @@ std::optional get_setting(GAME_SETTING setting) { if ((setting == GAME_SETTING::FREQUENCY_NUMERATOR || setting == GAME_SETTING::FREQUENCY_DENOMINATOR) && get_setting(GAME_SETTING::WINDOW_MODE) == 1u) { - size_t renderer = RenderAPI::get().renderer(); - return *reinterpret_cast(renderer + (setting == GAME_SETTING::FREQUENCY_NUMERATOR ? 0x10 : 0x14)); + auto game_api = GameAPI::get(); + if (setting == GAME_SETTING::FREQUENCY_NUMERATOR) + return game_api->renderer->fps; + else + return game_api->renderer->fps_denominator; } if (SettingData* data = get_setting_data(setting)) diff --git a/src/game_api/spawn_api.cpp b/src/game_api/spawn_api.cpp index 7cf829e97..4024ce175 100644 --- a/src/game_api/spawn_api.cpp +++ b/src/game_api/spawn_api.cpp @@ -12,23 +12,25 @@ #include // for pair, identity, min, _Find_fn, find #include // for vector, allocator, _Vector_iterator -#include "entities_chars.hpp" // for Player -#include "entities_items.hpp" // for ClimbableRope -#include "entities_liquids.hpp" // for Lava -#include "entities_monsters.hpp" // for Shopkeeper, RoomOwner -#include "entity.hpp" // for to_id, Entity, get_entity_ptr, Enti... -#include "items.hpp" // -#include "layer.hpp" // for Layer, g_level_max_y, g_level_max_x -#include "level_api.hpp" // for LevelGenSystem, ThemeInfo -#include "logger.h" // for DEBUG -#include "math.hpp" // for AABB -#include "memory.hpp" // for write_mem_prot, memory_read -#include "prng.hpp" // for PRNG, PRNG::PRNG_CLASS, PRNG::ENTIT... -#include "script/events.hpp" // for post_entity_spawn, pre_entity_spawn -#include "search.hpp" // for get_address -#include "state.hpp" // for enum_to_layer, State, StateMemory -#include "state_structs.hpp" // for LiquidTileSpawnData, LiquidPhysics -#include "util.hpp" // for OnScopeExit +#include "containers/custom_vector.hpp" // +#include "entities_chars.hpp" // for Player +#include "entities_items.hpp" // for ClimbableRope +#include "entities_liquids.hpp" // for Lava +#include "entities_monsters.hpp" // for Shopkeeper, RoomOwner +#include "entity.hpp" // for to_id, Entity, get_entity_ptr, Enti... +#include "illumination.hpp" // +#include "items.hpp" // +#include "layer.hpp" // for Layer, g_level_max_y, g_level_max_x +#include "level_api.hpp" // for LevelGenSystem, ThemeInfo +#include "logger.h" // for DEBUG +#include "math.hpp" // for AABB +#include "memory.hpp" // for write_mem_prot, memory_read +#include "prng.hpp" // for PRNG, PRNG::PRNG_CLASS, PRNG::ENTIT... +#include "script/events.hpp" // for post_entity_spawn, pre_entity_spawn +#include "search.hpp" // for get_address +#include "state.hpp" // for enum_to_layer, State, StateMemory +#include "state_structs.hpp" // for LiquidTileSpawnData, LiquidPhysics +#include "util.hpp" // for OnScopeExit struct Items; @@ -70,15 +72,20 @@ void spawn_liquid(ENT_TYPE entity_type, float x, float y) for (Lava* lava : lavas) { - float position[2] = {lava->x, lava->y}; - float color[4] = {1.782f, 0.575262f, 0.0f, 0.0f}; // green value is randomized! - float light_size = 1.0f; - uint32_t flags = 0x63; + Vec2 position{lava->x, lava->y}; + Color color{1.782f, 0.575262f, 0.0f, 0.0f}; + // green value is randomized in game + // uses PRNG_CLASS::LEVEL_DECO + // green = (PRNG::random_float / 1000 * 0.3 + 0.2) / 1.62 - using construct_illumination_ptr_fun_t = Illumination*(std::vector*, float*, float*, uint8_t, float, uint32_t, uint32_t, uint8_t); - static auto construct_illumination_ptr_call = (construct_illumination_ptr_fun_t*)get_address("generate_illumination"); + float light_size = 0.6f; // should be 0.8f for dark level + uint8_t flags = 0x63; - auto ill_ptr = construct_illumination_ptr_call(state->lightsources, position, color, 2, light_size, flags, lava->uid, lava->layer); + // using construct_illumination_ptr_fun_t = Illumination*(custom_vector*, float*, float*, uint8_t, float, uint32_t, uint32_t, uint8_t); + // static auto construct_illumination_ptr_call = (construct_illumination_ptr_fun_t*)get_address("generate_illumination"); + + // auto ill_ptr = construct_illumination_ptr_call(state->lightsources, position, color, 2, light_size, flags, lava->uid, lava->layer); + auto ill_ptr = create_illumination(position, color, LIGHT_TYPE::FOLLOW_ENTITY, light_size, flags, lava->uid, (LAYER)lava->layer); lava->emitted_light = ill_ptr; } } @@ -166,7 +173,7 @@ int32_t spawn_entity_abs(ENT_TYPE entity_type, float x, float y, LAYER layer, fl std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, vx, vy, false)->uid; + return State::get().layer(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, vx, vy, false)->uid; } int32_t spawn_entity_snap_to_floor(ENT_TYPE entity_type, float x, float y, LAYER layer) @@ -178,7 +185,7 @@ int32_t spawn_entity_snap_to_floor(ENT_TYPE entity_type, float x, float y, LAYER std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_entity_snap_to_floor(entity_type, x + offset_position.first, y + offset_position.second)->uid; + return State::get().layer(actual_layer)->spawn_entity_snap_to_floor(entity_type, x + offset_position.first, y + offset_position.second)->uid; } int32_t spawn_entity_snap_to_grid(ENT_TYPE entity_type, float x, float y, LAYER layer) @@ -190,7 +197,7 @@ int32_t spawn_entity_snap_to_grid(ENT_TYPE entity_type, float x, float y, LAYER std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, 0.0f, 0.0f, true)->uid; + return State::get().layer(actual_layer)->spawn_entity(entity_type, x + offset_position.first, y + offset_position.second, false, 0.0f, 0.0f, true)->uid; } int32_t spawn_entity_abs_nonreplaceable(ENT_TYPE entity_type, float x, float y, LAYER layer, float vx, float vy) @@ -215,7 +222,7 @@ int32_t spawn_entity_over(ENT_TYPE entity_type, uint32_t over_uid, float x, floa if (layer > 1) return -1; - return state.layer_local(layer)->spawn_entity_over(entity_type, overlay, x, y)->uid; + return state.layer(layer)->spawn_entity_over(entity_type, overlay, x, y)->uid; } int32_t spawn_door_abs(float x, float y, LAYER layer, uint8_t w, uint8_t l, uint8_t t) @@ -227,7 +234,7 @@ int32_t spawn_door_abs(float x, float y, LAYER layer, uint8_t w, uint8_t l, uint std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_door(x + offset_position.first, y + offset_position.second, w, l, t)->uid; + return State::get().layer(actual_layer)->spawn_door(x + offset_position.first, y + offset_position.second, w, l, t)->uid; } void spawn_backdoor_abs(float x, float y) @@ -238,8 +245,8 @@ void spawn_backdoor_abs(float x, float y) auto state = State::get(); DEBUG("Spawning backdoor on {}, {}", x, y); - Layer* front_layer = state.layer_local(0); - Layer* back_layer = state.layer_local(1); + Layer* front_layer = state.layer(0); + Layer* back_layer = state.layer(1); front_layer->spawn_entity(to_id("ENT_TYPE_FLOOR_DOOR_LAYER"), x, y, false, 0.0, 0.0, true); back_layer->spawn_entity(to_id("ENT_TYPE_FLOOR_DOOR_LAYER"), x, y, false, 0.0, 0.0, true); front_layer->spawn_entity(to_id("ENT_TYPE_LOGICAL_PLATFORM_SPAWNER"), x, y - 1.0f, false, 0.0, 0.0, true); @@ -255,7 +262,7 @@ int32_t spawn_apep(float x, float y, LAYER layer, bool right) std::pair offset_position; uint8_t actual_layer = enum_to_layer(layer, offset_position); - return State::get().layer_local(actual_layer)->spawn_apep(x + offset_position.first, y + offset_position.second, right)->uid; + return State::get().layer(actual_layer)->spawn_apep(x + offset_position.first, y + offset_position.second, right)->uid; } int32_t spawn_tree(float x, float y, LAYER layer) @@ -274,7 +281,7 @@ int32_t spawn_tree(float x, float y, LAYER layer, uint16_t height) x = std::roundf(x + offset_position.first); y = std::roundf(y + offset_position.second); - Layer* layer_ptr = State::get().layer_local(actual_layer); + Layer* layer_ptr = State::get().layer(actual_layer); // Needs some space on top if (x < 0 || static_cast(x) >= g_level_max_x || y < 1 || static_cast(y) + 2 >= g_level_max_y || height == 1 || @@ -358,7 +365,7 @@ int32_t spawn_mushroom(float x, float y, LAYER l, uint16_t height) // height rel std::pair offset(0.0f, 0.0f); const auto actual_layer = enum_to_layer(l, offset); - const auto layer_ptr = State::get().layer_local(actual_layer); + const auto layer_ptr = State::get().layer(actual_layer); const uint32_t i_x = static_cast(x + offset.first + 0.5f); uint32_t i_y = static_cast(y + offset.second + 0.5f); static const auto base = to_id("ENT_TYPE_FLOOR_MUSHROOM_BASE"); @@ -427,7 +434,7 @@ int32_t spawn_unrolled_player_rope(float x, float y, LAYER layer, TEXTURE textur std::pair offset(0.0f, 0.0f); const auto actual_layer = enum_to_layer(layer, offset); - const auto layer_ptr = State::get().layer_local(actual_layer); + const auto layer_ptr = State::get().layer(actual_layer); const uint32_t i_x = static_cast(x + offset.first + 0.5f); const uint32_t i_y = static_cast(y + offset.second + 0.5f); const float g_x = static_cast(i_x); diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 668e7737a..5873fedc1 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -13,6 +13,7 @@ #include "entities_chars.hpp" // for Player #include "entity.hpp" // for to_id, Entity, HookWithId, EntityDB #include "entity_hooks_info.hpp" // for Player +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager, SaveR... #include "game_patches.hpp" // #include "items.hpp" // for Items, SelectPlayerSlot @@ -280,6 +281,7 @@ State& State::get() } auto addr_location = get_address("state_location"); STATE = State{addr_location}; + get_is_init() = true; if (get_do_hooks()) { @@ -310,7 +312,6 @@ State& State::get() DEBUG("Not applying patches, someone has already done it"); } } - get_is_init() = true; } return STATE; } @@ -332,6 +333,12 @@ StateMemory* State::ptr_local() const return p.decode_local(); } +float get_zoom_level() +{ + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); +} + std::pair State::click_position(float x, float y) { float cz = get_zoom_level(); @@ -350,40 +357,6 @@ std::pair State::screen_position(float x, float y) return {rx, ry}; } -size_t State::get_zoom_level_address() -{ - static const size_t obj1 = get_address("zoom_level"); - static const size_t zoom_level_offset = get_address("zoom_level_offset"); - size_t obj2 = memory_read(obj1); - if (obj2 == 0) - { - return 0; - } - - size_t obj3 = memory_read(obj2 + 0x10); - if (obj3 == 0) - { - return 0; - } - return obj3 + zoom_level_offset; -} - -float State::get_zoom_level() -{ - static size_t offset = 0; - if (offset == 0) - { - auto addr = get_zoom_level_address(); - if (addr == 0) - { - return 13.5; - } - offset = addr - 4; - } - auto state = State::get().ptr(); - return memory_read(offset) + get_layer_zoom_offset(state->camera_layer); -} - void State::zoom(float level) { auto roomx = ptr()->w; @@ -434,11 +407,8 @@ void State::zoom(float level) write_mem_prot(zoom_telescope, level_str, true); // overwrite the current value - auto zla = get_zoom_level_address(); - if (zla != 0) - { - write_mem_prot(zla, level_str, true); - } + auto game_api = GameAPI::get(); + game_api->set_zoom(std::nullopt, level); } void StateMemory::force_current_theme(uint32_t t) @@ -724,7 +694,7 @@ uint8_t enum_to_layer(const LAYER layer) auto player = state->items->player(static_cast(std::abs((int)layer) - 1)); if (player != nullptr) { - return player->layer; + return player->layer > 1 ? 0 : player->layer; } } return 0; diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index d74a015b1..2f694a1de 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -6,8 +6,9 @@ #include // for pair #include // for vector -#include "aliases.hpp" // for ENT_TYPE, LAYER -#include "state_structs.hpp" // for JournalProgressStickerSlot, Illumination (p... +#include "aliases.hpp" // for ENT_TYPE, LAYER +#include "containers/custom_vector.hpp" // +#include "state_structs.hpp" // for JournalProgressStickerSlot, ... class Entity; class ScreenArenaIntro; @@ -43,19 +44,22 @@ struct Layer; struct LevelGenSystem; class ThemeInfo; struct Items; +struct Illumination; void fix_liquid_out_of_bounds(); #pragma pack(push, 1) // disable struct padding struct StateMemory { + using SCREEN = uint32_t; + size_t p00; /// Previous SCREEN, used to check where we're coming from when loading another SCREEN - uint32_t screen_last; + SCREEN screen_last; /// Current SCREEN, generally read-only or weird things will happen - uint32_t screen; + SCREEN screen; /// Next SCREEN, used to load the right screen when loading. Can be changed in PRE_LOAD_SCREEN to go somewhere else instead. Also see `state.loading`. - uint32_t screen_next; + SCREEN screen_next; /// Shows the current loading state (0=Not loading, 1=Fadeout, 2=Loading, 3=Fadein). Writing 1 or 2 will trigger a screen load to `screen_next`. uint32_t loading; /// The global level illumination, very big and bright. @@ -73,7 +77,7 @@ struct StateMemory /// Is 1 when you are in a level, but going to options sets it to 0 and does not set it back to 1 after the way back, don't trust it uint8_t playing; /// 8bit flags, multiple might be active at the same time - /// 1: Menu: Pauses the level timer and engine. Can't set, controller by the menu. + /// 1: Menu: Pauses the level timer and engine. Can't set, controlled by the menu. /// 2: Fade/Loading: Pauses all timers and engine. /// 4: Cutscene: Pauses total/level time but not engine. Used by boss cutscenes. /// 8: Unknown: Pauses total/level time and engine. Does not pause the global counter so set_global_interval still runs. @@ -104,15 +108,15 @@ struct StateMemory /// 0 - none, 1 - item, 3 - kapala int8_t kali_gifts; int32_t outposts_spawned; - /// Total negative amount spent in shops during the run
- /// The total money currently available (in single player) is `players[1].inventory.money + players[1].inventory.collected_money_total + state.money_shop_total` + /// Total amount spent in shops and sold idols during the run
+ /// The total money currently available is `loop (players[].inventory.money + players[].inventory.collected_money_total) + state.money_shop_total` int32_t money_shop_total; /// World number to start new runs in uint8_t world_start; /// Level number to start new runs in uint8_t level_start; /// THEME to start new runs in - uint8_t theme_start; + THEME theme_start; uint8_t b5f; /// Current seed in seeded mode, just set to a funny value and does nothing in adventure mode uint32_t seed; @@ -170,7 +174,7 @@ struct StateMemory std::array journal_progress_stain_slots; uint8_t journal_progress_theme_count; /// visited themes in journal progress page - std::array journal_progress_theme_slots; + std::array journal_progress_theme_slots; uint8_t unknown3; uint8_t unknown4; uint8_t unknown5a; @@ -249,8 +253,8 @@ struct StateMemory QuestsInfo* quests; AITarget* ai_targets; // e.g. hired hand uid -> snake uid LiquidPhysics* liquid_physics; - std::vector* particle_emitters; - std::vector* lightsources; + custom_vector* particle_emitters; + custom_vector* lightsources; EntityLookup* entity_lookup; // This is a Robin Hood Table @@ -258,10 +262,12 @@ struct StateMemory uint32_t padding13; RobinHoodTableEntry* uid_to_entity_data; - custom_vector> backlayer_player_related1; // inside vector: player and destination layer? - uint32_t layer_transition_effect_timer; - /// The currently drawn layer, can't be changed - uint8_t camera_layer; + custom_vector> entities_switching_layer; // inside vector: entity and destination layer + // only entities that go thru the layer (char_*, ghost, doesn't care about held items) + // does not care about the entity:set_layer() either + + uint32_t layer_transition_timer; + uint8_t transition_to_layer; uint8_t unknown31a; // padding probably uint8_t unknown31b; uint8_t unknown31c; @@ -269,15 +275,27 @@ struct StateMemory RoomOwnersInfo room_owners; /// Number of frames since the game was launched uint32_t time_startup; - uint32_t special_visibility_flags; + + union + { + uint32_t special_visibility_flags; // it's actually four 8bit values, last one is not even a flag, it's just layer + + struct + { + uint8_t crust_visibility; + uint8_t compass_visibility; + uint8_t special_compass_visibility; + uint8_t camera_layer; + }; + }; + /// Camera bounds and position Camera* camera; uint8_t unknown40; int8_t unknown41; // other character related (hired hand, basecamp characters) uint8_t unknown42; uint8_t unknown43; - uint32_t unknown44; - uint64_t unknown45; + uint32_t unknown44; // probably padding /// This function should only be used in a very specific circumstance (forcing the exiting theme when manually transitioning). Will crash the game if used inappropriately! void force_current_theme(uint32_t t); @@ -309,21 +327,16 @@ struct State StateMemory* ptr() const; StateMemory* ptr_local() const; + // use only if you only want the layer, otherwise use `ptr()->layers` Layer* layer(uint8_t index) const { return ptr()->layers[index]; } - Layer* layer_local(uint8_t index) const - { - return ptr_local()->layers[index]; - } void godmode(bool g); void godmode_companions(bool g); void darkmode(bool g); - static size_t get_zoom_level_address(); - static float get_zoom_level(); void zoom(float level); static std::pair click_position(float x, float y); diff --git a/src/game_api/state_structs.hpp b/src/game_api/state_structs.hpp index e913ddb32..0e7fbeb1a 100644 --- a/src/game_api/state_structs.hpp +++ b/src/game_api/state_structs.hpp @@ -19,55 +19,6 @@ struct RobinHoodTableEntry Entity* entity; }; -struct LightParams -{ - float red; // default = 1.0 (can go over 1.0 for oversaturation) - float green; - float blue; - float size; -}; - -struct Illumination -{ - union - { - /// Table of light1, light2, ... etc. - std::array lights; - struct - { - LightParams light1; - LightParams light2; - LightParams light3; - /// It's rendered on objects around, not as an actual bright spot - LightParams light4; - }; - }; - float brightness; - float brightness_multiplier; - float light_pos_x; - float light_pos_y; - float offset_x; - float offset_y; - float distortion; - int32_t entity_uid; - uint32_t timer; - union - { - /// see [flags.hpp](https://github.com/spelunky-fyi/overlunky/blob/main/src/game_api/flags.hpp) illumination_flags - uint32_t flags; - struct - { - uint8_t light_flags; // no reason to expose this - - /// Only one can be set: 1 - Follow camera, 2 - Follow Entity, 3 - Rectangle, full brightness - /// Rectangle always uses light1, even when it's disabled in flags - uint8_t type_flags; - uint8_t layer; - bool enabled; - }; - }; -}; - struct InputMapping { uint8_t jump; diff --git a/src/game_api/strings.cpp b/src/game_api/strings.cpp index b2fd9e8e5..b7d1a9720 100644 --- a/src/game_api/strings.cpp +++ b/src/game_api/strings.cpp @@ -100,7 +100,6 @@ void OnToast(char16_t* buffer) void strings_init() { g_original_string_ids_end = get_type(1)->description; // get wrong stringid from bordertile - // fix_entity_descriptions(g_original_string_ids_end); // disabled until someone wants to work on this auto addr_format_shopitem = Memory::get().at_exe(get_virtual_function_address(VTABLE_OFFSET::ITEM_PICKUP_ROPEPILE, 7)); auto addr_npcdialogue = get_address("speech_bubble_fun"); diff --git a/src/game_api/texture.cpp b/src/game_api/texture.cpp index bb495b842..3cbd0a5b1 100644 --- a/src/game_api/texture.cpp +++ b/src/game_api/texture.cpp @@ -261,8 +261,6 @@ void reload_texture(const char* texture_name) } void reload_texture(const char** texture_name) { - class Renderer; - using LoadTextureFunT = void(Renderer*, const char**); auto& render = RenderAPI::get(); @@ -272,7 +270,7 @@ void reload_texture(const char** texture_name) // to the wanted function static constexpr size_t c_LoadTextureVirtualIndex = 0x2E; - auto renderer_ptr = (Renderer*)render.renderer(); + auto renderer_ptr = render.renderer(); auto load_texture = *vtable_find(renderer_ptr, c_LoadTextureVirtualIndex); load_texture(renderer_ptr, texture_name); } diff --git a/src/game_api/virtual_table.cpp b/src/game_api/virtual_table.cpp index f256fffed..3d1fd0ea3 100644 --- a/src/game_api/virtual_table.cpp +++ b/src/game_api/virtual_table.cpp @@ -15,3 +15,9 @@ size_t get_virtual_function_address(VTABLE_OFFSET table_entry, uint32_t function size_t* func_address = reinterpret_cast(first_table_entry + ((static_cast(table_entry) + function_index) * sizeof(size_t))); return *func_address - mem.exe_ptr; } + +size_t get_virtual_function_address(void* object, uint32_t function_index) +{ + auto v_table = *static_cast(object); + return *(v_table + function_index); +} diff --git a/src/game_api/virtual_table.hpp b/src/game_api/virtual_table.hpp index 8a4a3c728..ed368b9ce 100644 --- a/src/game_api/virtual_table.hpp +++ b/src/game_api/virtual_table.hpp @@ -990,3 +990,4 @@ enum class VTABLE_OFFSET }; size_t get_virtual_function_address(VTABLE_OFFSET table_entry, uint32_t function_index); +size_t get_virtual_function_address(void* object, uint32_t function_index); diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index f5cf8ef56..9f6151730 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -39,6 +39,7 @@ #include "file_api.hpp" #include "flags.hpp" #include "game_manager.hpp" +#include "illumination.hpp" #include "items.hpp" #include "level_api.hpp" #include "logger.h" @@ -3479,6 +3480,24 @@ void render_light(const char* name, LightParams* light) ImGui::PopID(); } +template +void render_flags(const std::array names_array, T* flag_field, bool show_number = true) +{ + for (int idx{0}; idx < SIZE && idx < sizeof(T) * 8; ++idx) + { + // just simplified version of CheckboxFlagsT + + T value = (T)std::pow(2, idx); + bool on = (*flag_field & value) == value; + + if (names_array[idx][0] != '\0' && + ImGui::Checkbox(show_number ? fmt::format("{}: {}", idx + 1, names_array[idx]).c_str() : names_array[idx], &on)) + { + *flag_field ^= value; + } + } +} + void render_illumination(Illumination* light, const char* sect = "") { ImGui::PushID(sect); @@ -3497,10 +3516,7 @@ void render_illumination(Illumination* light, const char* sect = "") ImGui::InputFloat("Offset X##LightPosX", &light->offset_x); ImGui::InputFloat("Offset Y##LightPosY", &light->offset_y); ImGui::DragFloat("Distortion##LightDistortion", &light->distortion, 0.01f); - for (int i = 0; i < 11; i++) - { - ImGui::CheckboxFlags(illumination_flags[i], &light->flags, (int)std::pow(2, i)); - } + render_flags(illumination_flags, &light->flags); ImGui::PopID(); } @@ -5791,10 +5807,7 @@ void render_options() if (submenu("Frame advance / Engine pause type")) { ImGui::PushID("PauseType"); - for (int i = 1; i < 7; i++) - { - ImGui::CheckboxFlags(pause_types[i], &g_pause_type, (int)std::pow(2, i)); - } + render_flags(pause_types, &g_pause_type, false); ImGui::PopID(); endmenu(); } @@ -7118,10 +7131,7 @@ void render_entity_props(int uid, bool detached = false) ImGui::InputScalar("Search flags##SearchFlags", ImGuiDataType_U32, &entity->type->search_flags, 0, 0, "%p", ImGuiInputTextFlags_ReadOnly); if (submenu("Properties flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(entity_type_properties_flags[i], &entity->type->properties_flags, (int)std::pow(2, i)); - } + render_flags(entity_type_properties_flags, &entity->type->properties_flags); endmenu(); } endmenu(); @@ -7315,18 +7325,12 @@ void render_entity_props(int uid, bool detached = false) } if (submenu("Flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(entity_flags[i], &entity->flags, (int)std::pow(2, i)); - } + render_flags(entity_flags, &entity->flags); endmenu(); } if (submenu("More Flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(more_flags[i], &entity->more_flags, (int)std::pow(2, i)); - } + render_flags(more_flags, &entity->more_flags); endmenu(); } if (is_movable && submenu("Input Display")) @@ -7537,7 +7541,7 @@ struct TextureViewer static TextureViewer texture_viewer{0, -1}; void render_vanilla_stuff() { - if (peek_layer && g_state->layer_transition_effect_timer == 0) + if (peek_layer && g_state->layer_transition_timer == 0) { uint8_t other_layer = g_state->camera_layer ? 0 : 1; auto [bbox_left, bbox_top] = UI::click_position(-1.0f, 1.0f); @@ -8082,67 +8086,37 @@ void render_game_props() } if (submenu("Level flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(level_flags[i], &g_state->level_flags, (int)std::pow(2, i)); - } + render_flags(level_flags, &g_state->level_flags); endmenu(); } if (submenu("Quest flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(quest_flags[i], &g_state->quest_flags, (int)std::pow(2, i)); - } + render_flags(quest_flags, &g_state->quest_flags); endmenu(); } if (submenu("Journal flags")) { - for (int i = 0; i < 21; i++) - { - ImGui::CheckboxFlags(journal_flags[i], &g_state->journal_flags, (int)std::pow(2, i)); - } + render_flags(journal_flags, &g_state->journal_flags); endmenu(); } if (submenu("Presence flags")) { - for (int i = 0; i < 11; i++) - { - ImGui::CheckboxFlags(presence_flags[i], &g_state->presence_flags, (int)std::pow(2, i)); - } + render_flags(presence_flags, &g_state->presence_flags); endmenu(); } if (submenu("Special visibility flags")) { - for (int i = 0; i < 32; i++) - { - ImGui::CheckboxFlags(special_visibility_flags[i], &g_state->special_visibility_flags, (int)std::pow(2, i)); - } + render_flags(special_visibility_flags, &g_state->special_visibility_flags); endmenu(); } if (submenu("Level generation flags")) { - auto flags = (int)g_state->level_gen->flags; - auto flags2 = (int)g_state->level_gen->flags2; - auto flags3 = (int)g_state->level_gen->flags3; ImGui::SeparatorText("Flags 1"); - for (int i = 0; i < 8; i++) - { - ImGui::CheckboxFlags(levelgen_flags[i], &flags, (int)std::pow(2, i)); - } + render_flags(levelgen_flags, &g_state->level_gen->flags); ImGui::SeparatorText("Flags 2"); - for (int i = 0; i < 8; i++) - { - ImGui::CheckboxFlags(levelgen_flags2[i], &flags2, (int)std::pow(2, i)); - } + render_flags(levelgen_flags2, &g_state->level_gen->flags2); ImGui::SeparatorText("Flags 3"); - for (int i = 0; i < 8; i++) - { - ImGui::CheckboxFlags(levelgen_flags3[i], &flags3, (int)std::pow(2, i)); - } - g_state->level_gen->flags = (uint8_t)flags; - g_state->level_gen->flags2 = (uint8_t)flags2; - g_state->level_gen->flags3 = (uint8_t)flags3; + render_flags(levelgen_flags3, &g_state->level_gen->flags3); if (g_state->current_theme) { ImGui::SeparatorText("Theme flags"); diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index 2f073f953..8a31c699c 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -14,7 +14,9 @@ #include "entities_mounts.hpp" // for Mount #include "entity.hpp" // for to_id, Entity, get_entity_ptr #include "entity_lookup.hpp" // +#include "game_api.hpp" // #include "game_manager.hpp" // for get_game_manager, GameManager +#include "illumination.hpp" // #include "items.hpp" // for Items #include "layer.hpp" // for Layer, EntityList::Range, Entit... #include "level_api.hpp" // for LevelGenSystem @@ -38,7 +40,7 @@ void UI::godmode_companions(bool g) } void UI::death_enabled(bool g) { - set_death_enabled(g); + set_level_logic_enabled(g); } std::pair UI::click_position(float x, float y) { @@ -83,7 +85,8 @@ void UI::transition(uint8_t world, uint8_t level, uint8_t theme) } float UI::get_zoom_level() { - return State::get_zoom_level(); + auto game_api = GameAPI::get(); + return game_api->get_current_zoom(); } void UI::teleport(float x, float y, bool s, float vx, float vy, bool snap) { @@ -767,5 +770,5 @@ std::pair UI::spawn_position() void UI::load_death_screen() { - call_death_screen(); + ::load_death_screen(); }