diff --git a/README.md b/README.md index 1fa4253c1..61bebca00 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Current features and their *default* keyboard shortcuts. + **Ctrl+,**: Zoom in + **Ctrl+.**: Zoom out + **Ctrl+0**: Auto fit level width to screen + + **Ctrl+1**: Reset zoom level to default for all areas + **Ctrl+2345**: Zoom to X level width + **Shift+IJKL**: Move camera in desired direction + **Shift+U**: Reset camera focus and bounds @@ -159,7 +160,10 @@ Lua scripting is still buggy and unfinished and the **API might change**, althou + Try toggling the *Capture third-party overlays* option in *Game Capture*. It's possible to hide OL if running the game through Steam. + If you have lag or crashes, check previous question. - If you **don't have scripts** in the Scripts tab - + You didn't put them in the right place. [Follow](#installation-and-usage) the [instructions](#scripts) or use Modlunky2. + + You didn't put them in the right place. [Follow](#installation-and-usage) the [instructions](#scripts), use Modlunky2 or change the script directory in the options. + - If some of the keyboard shortcuts don't work + + Make sure your keyboard is not [ghosting](https://www.microsoft.com/applied-sciences/projects/anti-ghosting-demo) the key combo. + + Make sure Windows is not using the keys for [something stupid](https://superuser.com/questions/1367843/how-to-disable-ctrlshift-keyboard-layout-switch-for-the-same-input-language-i). - If it still doesn't work + Stop by #s2-modding-help on the [community Discord](https://discord.gg/spelunky-community) or [submit an issue](https://github.com/spelunky-fyi/overlunky/issues) + We want to know your OS version, are you using the game on Steam, what you have tried, what tools you are using, how you are using them and all the versions. These can be found in the terminal output or ingame overlays. ("Latest" or WHIP is not a version, but 7f0bc94 or 0.6.1 is.) diff --git a/docs/game_data/lua_enums.txt b/docs/game_data/lua_enums.txt index f8219d697..6f63cbe45 100644 --- a/docs/game_data/lua_enums.txt +++ b/docs/game_data/lua_enums.txt @@ -327,29 +327,56 @@ DYNAMIC_TEXTURE = { } ENTITY_OVERRIDE = { ACTIVATE = 25, - APPLY_MOVEMENT = 71, + APPLY_FRICTION = 84, CAN_BE_PUSHED = 10, - CHECK_IS_FALLING = 75, + CAN_ENTER = 46, + CAN_JUMP = 37, + CHECK_OUT_OF_BOUNDS = 58, + CLONED_TO = 63, + COLLECT_TREASURE = 70, CREATE_RENDERING_INFO = 1, + CRUSH = 90, DAMAGE = 48, DESTROY = 5, + DROP = 69, DTOR = 0, + ENTER = 42, + ENTER_ATTEMPT = 40, + FALL = 83, FLOOR_UPDATE = 38, + FREEZE = 52, FRICTION = 17, GET_HELD_ENTITY = 22, + HIDE_HUD = 41, INIT = 36, + INITIALIZE = 75, IS_IN_LIQUID = 12, + IS_ON_FIRE = 45, + IS_UNLOCKED = 45, KILL = 3, LEDGE_GRAB = 31, + LIGHT_LEVEL = 44, + LIGHT_ON_FIRE = 53, ON_COLLISION1 = 4, ON_COLLISION2 = 26, - PROCESS_INPUT = 77, + ON_HIT = 49, + PICKED_UP = 80, + PICKED_UP_BY = 68, + PICK_UP = 67, + PROCESS_INPUT = 78, + SET_CURSED = 54, SET_INVISIBLE = 15, + STANDING_ON = 60, + STOMPED_BY = 61, + STOMP_DAMAGE = 43, STOOD_ON = 32, + STUN = 51, + THROWN_BY = 62, TRIGGER_ACTION = 24, UPDATE_STATE_MACHINE = 2, WALKED_OFF = 30, - WALKED_ON = 29 + WALKED_ON = 29, + WEB_COLLISION = 55 } ENT_FLAG = { CAN_BE_STOMPED = 15, @@ -1842,6 +1869,136 @@ JUNGLESISTERS = { PARSNIP_RESCUED = 2, WARNING_ONE_WAY_DOOR = 4 } +KEY = { + ["0"] = 48, + ["1"] = 49, + ["2"] = 50, + ["3"] = 51, + ["4"] = 52, + ["5"] = 53, + ["6"] = 54, + ["7"] = 55, + ["8"] = 56, + A = 65, + ADD = 107, + ALT = 18, + B = 66, + BACKSPACE = 8, + C = 67, + CAPS = 20, + CLEAR = 12, + COMMA = 188, + CTRL = 17, + D = 68, + DECIMAL = 110, + DELETE = 46, + DIVIDE = 111, + DOWN = 40, + E = 69, + END = 35, + ESCAPE = 27, + EXECUTE = 43, + F = 70, + F1 = 112, + F2 = 113, + F3 = 114, + F4 = 115, + F5 = 116, + F6 = 117, + F7 = 118, + F8 = 119, + F9 = 120, + F10 = 121, + F11 = 122, + F12 = 123, + F13 = 124, + F14 = 125, + F15 = 126, + F16 = 127, + F17 = 128, + F18 = 129, + F19 = 130, + F20 = 131, + F21 = 132, + F22 = 133, + F23 = 134, + F24 = 135, + G = 71, + H = 72, + HOME = 36, + I = 73, + INSERT = 45, + J = 74, + K = 75, + L = 76, + LALT = 164, + LCONTROL = 162, + LEFT = 37, + LSHIFT = 160, + M = 77, + MINUS = 189, + MULTIPLY = 106, + N = 78, + NUMPAD0 = 96, + NUMPAD1 = 97, + NUMPAD2 = 98, + NUMPAD3 = 99, + NUMPAD4 = 100, + NUMPAD5 = 101, + NUMPAD6 = 102, + NUMPAD7 = 103, + NUMPAD8 = 104, + NUMPAD9 = 105, + O = 79, + OEM_1 = 186, + OEM_2 = 191, + OEM_3 = 192, + OEM_4 = 219, + OEM_5 = 220, + OEM_6 = 221, + OEM_7 = 222, + OEM_8 = 223, + OEM_102 = 226, + OL_MOD_ALT = 2048, + OL_MOD_CTRL = 256, + OL_MOD_SHIFT = 512, + OL_MOUSE_1 = 1025, + OL_MOUSE_2 = 1026, + OL_MOUSE_3 = 1027, + OL_MOUSE_4 = 1028, + OL_MOUSE_5 = 1029, + OL_MOUSE_WHEEL_DOWN = 1041, + OL_MOUSE_WHEEL_UP = 1042, + P = 80, + PAUSE = 19, + PERIOD = 190, + PGDN = 34, + PGUP = 33, + PLUS = 187, + PRINT = 42, + Q = 81, + R = 82, + RALT = 165, + RCONTROL = 163, + RETURN = 13, + RIGHT = 39, + RSHIFT = 161, + S = 83, + SELECT = 41, + SEPARATOR = 108, + SHIFT = 16, + SNAPSHOT = 44, + SPACE = 32, + SUBTRACT = 109, + T = 84, + TAB = 9, + U = 85, + UP = 38, + V = 86, + W = 87, + X = 88, + Y = 89 +} LAYER = { BACK = 1, BOTH = -128, @@ -1878,6 +2035,36 @@ LIQUID_POOL = { STAGNANT_LAVA = 5, WATER = 1 } +LOGIC = { + APEP = 15, + ARENA_1 = 23, + ARENA_2 = 24, + ARENA_3 = 25, + ARENA_ALIEN_BLAST = 26, + ARENA_LOOSE_BOMBS = 27, + BLACK_MARKET = 21, + BUBBLER = 18, + COG_SACRIFICE = 16, + DICESHOP = 6, + DISCOVERY_INFO = 20, + DUAT_BOSSES = 17, + GHOST = 3, + GHOST_TOAST = 4, + JELLYFISH = 22, + MAGMAMAN_SPAWN = 11, + MOON_CHALLENGE = 8, + OLMEC_CUTSCENE = 13, + OUROBOROS = 1, + PLEASURE_PALACE = 19, + PRE_CHALLENGE = 7, + SPEEDRUN = 2, + STAR_CHALLENGE = 9, + SUN_CHALLENGE = 10, + TIAMAT_CUTSCENE = 14, + TUN_AGGRO = 5, + TUTORIAL = 0, + WATER_BUBBLES = 12 +} MASK = { ACTIVEFLOOR = 128, ANY = 0, @@ -1925,6 +2112,10 @@ ON = { ONLINE_LOBBY = 29, OPTIONS = 5, PLAYER_PROFILE = 6, + POST_LAYER_CREATION = 148, + POST_LAYER_DESTRUCTION = 152, + POST_LEVEL_CREATION = 146, + POST_LEVEL_DESTRUCTION = 150, POST_LEVEL_GENERATION = 112, POST_LOAD_JOURNAL_CHAPTER = 139, POST_LOAD_SCREEN = 136, @@ -1933,6 +2124,10 @@ ON = { PRE_GET_FEAT = 140, PRE_GET_RANDOM_ROOM = 113, PRE_HANDLE_ROOM_TILES = 114, + PRE_LAYER_CREATION = 147, + PRE_LAYER_DESTRUCTION = 151, + PRE_LEVEL_CREATION = 145, + PRE_LEVEL_DESTRUCTION = 149, PRE_LEVEL_GENERATION = 110, PRE_LOAD_JOURNAL_CHAPTER = 138, PRE_LOAD_LEVEL_FILES = 109, @@ -2349,6 +2544,11 @@ QUEST_FLAG = { VAULT_SPAWNED = 3, WADDLER_AGGROED = 10 } +RECURSIVE_MODE = { + EXCLUSIVE = 0, + INCLUSIVE = 1, + NONE = 2 +} RENDER_INFO_OVERRIDE = { DTOR = 0, RENDER = 3 @@ -3212,7 +3412,11 @@ TILE_CODE = { APEP = 379, APEP_LEFT = 380, APEP_RIGHT = 381, + ARROW_METAL = 393, + ARROW_METAL_POISON = 395, ARROW_TRAP = 55, + ARROW_WOODEN = 392, + ARROW_WOODEN_POISON = 394, ASSASSIN = 254, AUTOWALLTORCH = 100, BABYLON_FLOOR = 18, @@ -3232,7 +3436,7 @@ TILE_CODE = { BOMB_BOX = 284, BONE_BLOCK = 6, BONE_KEY = 305, - BOOMBOX = 392, + BOOMBOX = 397, BOOMERANG = 318, BOULDER = 378, BUBBLE_PLATFORM = 369, @@ -3315,7 +3519,7 @@ TILE_CODE = { EGGPLANT_CHILD = 159, EGGPLANT_CROWN = 302, EGGPLANT_DOOR = 179, - EGGPLUP = 407, + EGGPLUP = 412, EGGSAC = 344, EGGSAC_BOTTOM = 348, EGGSAC_LEFT = 345, @@ -3343,19 +3547,19 @@ TILE_CODE = { FORCEFIELD = 217, FORCEFIELD_HORIZONTAL = 333, FORCEFIELD_HORIZONTAL_TOP = 334, - FORCEFIELD_TIMED = 411, + FORCEFIELD_TIMED = 416, FORCEFIELD_TOP = 70, FOUNTAIN_DRAIN = 143, FOUNTAIN_HEAD = 142, FROG = 259, FROG_ORANGE = 260, - FURNITURE_CHAIR_LOOKING_LEFT = 400, - FURNITURE_CHAIR_LOOKING_RIGHT = 401, - FURNITURE_CONSTRUCTION_SIGN = 397, - FURNITURE_DININGTABLE = 402, - FURNITURE_DRESSER = 393, - FURNITURE_SIDETABLE = 404, - FURNITURE_SINGLEBED = 398, + FURNITURE_CHAIR_LOOKING_LEFT = 405, + FURNITURE_CHAIR_LOOKING_RIGHT = 406, + FURNITURE_CONSTRUCTION_SIGN = 402, + FURNITURE_DININGTABLE = 407, + FURNITURE_DRESSER = 398, + FURNITURE_SIDETABLE = 409, + FURNITURE_SINGLEBED = 403, GHIST_DOOR2 = 48, GHIST_PRESENT = 358, GHIST_SHOPKEEPER = 220, @@ -3388,7 +3592,7 @@ TILE_CODE = { HOUYIBOW = 205, HUMPHEAD = 331, HUNDUN = 261, - HUNDUN_SPIKES = 403, + HUNDUN_SPIKES = 408, ICEFLOOR = 152, IDOL = 186, IDOL_FLOOR = 187, @@ -3438,7 +3642,7 @@ TILE_CODE = { MOUNT_AXOLOTL = 329, MOUNT_QILIN = 330, MOUNT_ROCKDOG = 328, - MOVABLE_SPIKES = 391, + MOVABLE_SPIKES = 396, MUMMY = 133, MUSHROOM_BASE = 103, NECROMANCER = 136, @@ -3463,7 +3667,7 @@ TILE_CODE = { PALACE_SIGN = 359, PALACE_TABLE = 167, PALACE_TABLE_TRAY = 168, - PANGXIE = 410, + PANGXIE = 415, PARACHUTE = 297, PASTE = 294, PEN_FLOOR = 224, @@ -3480,9 +3684,9 @@ TILE_CODE = { POT = 108, POTOFGOLD = 90, POWDER_KEG = 54, - POWDER_KEG_TIMED = 412, + POWDER_KEG_TIMED = 417, PRESENT = 332, - PROTO_GENERATOR = 406, + PROTO_GENERATOR = 411, PROTO_SHOPKEEPER = 355, PUNISHBALL = 370, PUNISHBALL_ATTACH = 371, @@ -3492,7 +3696,7 @@ TILE_CODE = { PUNISHBALL_ATTACH_TOP = 387, PUSH_BLOCK = 53, QUICKSAND = 66, - QUILLBACK = 396, + QUILLBACK = 401, REDSKELETON = 244, REGENERATING_BLOCK = 31, ROBOT = 123, @@ -3525,7 +3729,7 @@ TILE_CODE = { SINGLEBED = 73, SISTER = 226, SKELETON = 243, - SKELETON_KEY = 395, + SKELETON_KEY = 400, SKULL = 390, SKULL_DROP_TRAP = 353, SLEEPING_HIREDHAND = 222, @@ -3536,7 +3740,7 @@ TILE_CODE = { SNAP_TRAP = 112, SORCERESS = 134, SPARK_TRAP = 163, - SPARROW = 405, + SPARROW = 410, SPECS = 289, SPIDER = 350, SPIDER_FALLING = 351, @@ -3545,7 +3749,7 @@ TILE_CODE = { SPIKEBALL_NO_BOUNCE = 377, SPIKEBALL_TRAP = 376, SPIKES = 51, - SPIKES_UPSIDEDOWN = 413, + SPIKES_UPSIDEDOWN = 418, SPRING_TRAP = 151, STARTING_EXIT = 44, STICKY_TRAP = 177, @@ -3563,7 +3767,7 @@ TILE_CODE = { TEMPLE_FLOOR = 16, THIEF = 228, THINICE = 153, - THIN_ICE = 394, + THIN_ICE = 399, THORN_VINE = 56, TIAMAT = 172, TIKIMAN = 118, @@ -3576,12 +3780,12 @@ TILE_CODE = { TREASURE_VAULTCHEST = 89, TREE_BASE = 102, TRUE_CROWN = 303, - TUN = 408, + TUN = 413, TURKEY = 104, TUTORIAL_MENU_SIGN = 274, TUTORIAL_SPEEDRUN_SIGN = 273, TV = 81, - UDJAT_CHEST = 399, + UDJAT_CHEST = 404, UDJAT_EYE = 298, UDJAT_KEY = 272, UDJAT_SOCKET = 194, @@ -3591,8 +3795,9 @@ TILE_CODE = { USHABTI = 71, VAMPIRE = 249, VAMPIRE_FLYING = 250, - VAN_HORSING = 409, + VAN_HORSING = 414, VAULT_WALL = 195, + VENOM = 391, VINE = 34, VLAD = 126, VLADS_CAPE = 308, diff --git a/docs/game_data/spel2.lua b/docs/game_data/spel2.lua index 63eef3c97..fab4efbe6 100644 --- a/docs/game_data/spel2.lua +++ b/docs/game_data/spel2.lua @@ -438,10 +438,13 @@ function god(g) end ---@param g boolean ---@return nil function god_companions(g) end ----Set the zoom level used in levels and shops. 13.5 is the default. +---Set the zoom level used in levels and shops. 13.5 is the default, or 12.5 for shops. See zoom_reset. ---@param level number ---@return nil function zoom(level) end +---Reset the default zoom levels for all areas and sets current zoom level to 13.5. +---@return nil +function zoom_reset() end ---Pause/unpause the game. ---This is just short for `state.pause == 32`, but that produces an audio bug ---I suggest `state.pause == 2`, but that won't run any callback, `state.pause == 16` will do the same but [set_global_interval](https://spelunky-fyi.github.io/overlunky/#set_global_interval) will still work @@ -735,9 +738,12 @@ function lock_door_at(x, y) end ---@param y number ---@return nil function unlock_door_at(x, y) end ----Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. +---Get the current frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter is paused if you block PRE_UPDATE from running, and also doesn't increment during some loading screens, even though state update still runs. ---@return integer function get_frame() end +---Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter keeps incrementing when state is updated, even during loading screens. +---@return integer +function get_global_frame() end ---Get the current timestamp in milliseconds since the Unix Epoch. ---@return nil function get_ms() end @@ -878,6 +884,11 @@ function get_aabb_bounds() end ---Gets the current camera position in the level ---@return number, number function get_camera_position() end +---Sets the absolute current camera position without rubberbanding animation. Ignores camera bounds or currently focused uid, but doesn't clear them. Best used in ON.RENDER_PRE_GAME or similar. See Camera for proper camera handling with bounds and rubberbanding. +---@param cx number +---@param cy number +---@return nil +function set_camera_position(cx, cy) end ---Set the nth bit in a number. This doesn't actually change the variable you pass, it just returns the new value you can use. ---@param flags Flags ---@param bit integer @@ -921,19 +932,6 @@ function test_mask(flags, mask) end ---Gets the resolution (width and height) of the screen ---@return integer, integer function get_window_size() end ----Steal input from a Player, HiredHand or PlayerGhost ----@param uid integer ----@return nil -function steal_input(uid) end ----Return input previously stolen with [steal_input](https://spelunky-fyi.github.io/overlunky/#steal_input) ----@param uid integer ----@return nil -function return_input(uid) end ----Send input to entity, has to be previously stolen with [steal_input](https://spelunky-fyi.github.io/overlunky/#steal_input) ----@param uid integer ----@param buttons INPUTS ----@return nil -function send_input(uid, buttons) end ---Clears a callback that is specific to a screen. ---@param screen_id integer ---@param cb_id CallbackId @@ -1306,6 +1304,10 @@ function create_layer(layer) end ---@param enable boolean ---@return nil function set_level_logic_enabled(enable) end +---Setting to true will stop the state update from unpausing after a screen load, leaving you with state.pause == PAUSE.FADE on the first frame to do what you want. +---@param enable boolean +---@return nil +function set_start_level_paused(enable) end ---Converts INPUTS to (x, y, BUTTON) ---@param inputs INPUTS ---@return number, number, BUTTON @@ -1798,6 +1800,9 @@ function set_feat_hidden(feat, hidden) end ---@param description string ---@return nil function change_feat(feat, hidden, name, description) end +---Returns the Bucket of data stored in shared memory between Overlunky and Playlunky +---@return Bucket +function get_bucket() end --## Types do @@ -2218,6 +2223,8 @@ do ---@field journal_ui JournalUI ---@field save_related SaveRelated ---@field main_menu_music BackgroundSound + ---@field buttons_controls integer[] @size: MAX_PLAYERS @Yet another place to get player inputs, in some format + ---@field buttons_movement integer[] @size: MAX_PLAYERS @Yet another place to get player inputs, in some format ---@class SaveRelated ---@field journal_popup_ui JournalPopupUI @@ -2233,8 +2240,12 @@ do ---@field slide_position number ---@class GameProps - ---@field buttons integer + ---@field buttons integer @Might be used for some menu inputs not found in buttons_menu + ---@field buttons_extra integer @Might be used for some menu inputs not found in buttons_menu + ---@field buttons_menu_previous MENU_INPUT @Previous state of buttons_menu + ---@field buttons_menu MENU_INPUT @Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in ON.PRE_UPDATE. ---@field game_has_focus boolean + ---@field modal_open integer ---@class PRNG ---@field seed fun(self, seed: integer): nil @Same as `seed_prng` @@ -6283,6 +6294,24 @@ function LogicMagmamanSpawn:remove_spawn(ms) end ---@field y integer ---@field timer integer +---@class Overlunky + ---@field options table @Current Overlunky options. Read only. + ---@field set_options table @Write some select options here to change Overlunky options. Just use the same keys as in options. + ---@field keys table @Current Overlunky key bindings. Read only. You can use this to bind some custom feature to the same unknown key as currently bound by the user. + ---@field ignore_keys unordered_Array @Disable some key bindings in Overlunky, whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. + ---@field ignore_keycodes unordered_Array @Disable keys that may or may not be used by Overlunky. You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. + ---@field ignore_features unordered_Array @TODO: Disable Overlunky features. Doesn't do anything yet. + ---@field selected_uid integer @Currently selected uid in the entity picker or -1 if nothing is selected. + ---@field set_selected_uid integer? @Set currently selected uid in the entity picker or -1 to clear selection. + ---@field selected_uids integer[] @Currently selected uids in the entity finder. + ---@field hovered_uid integer @Currently hovered entity uid or -1 if nothing is hovered. + ---@field set_selected_uid integer? @Set currently selected uid in the entity picker or -1 to clear selection. + ---@field set_selected_uids integer[] @size: ? @Set currently selected uids in the entity finder. + +---@class Bucket + ---@field data table @You can store arbitrary simple values here in Playlunky to be read in on Overlunky script for example. + ---@field overlunky Overlunky @Access Overlunky options here, nil if Overlunky is not loaded. + end --## Static class functions @@ -6797,29 +6826,56 @@ DYNAMIC_TEXTURE = { ---@alias DYNAMIC_TEXTURE integer ENTITY_OVERRIDE = { ACTIVATE = 25, - APPLY_MOVEMENT = 71, + APPLY_FRICTION = 84, CAN_BE_PUSHED = 10, - CHECK_IS_FALLING = 75, + CAN_ENTER = 46, + CAN_JUMP = 37, + CHECK_OUT_OF_BOUNDS = 58, + CLONED_TO = 63, + COLLECT_TREASURE = 70, CREATE_RENDERING_INFO = 1, + CRUSH = 90, DAMAGE = 48, DESTROY = 5, + DROP = 69, DTOR = 0, + ENTER = 42, + ENTER_ATTEMPT = 40, + FALL = 83, FLOOR_UPDATE = 38, + FREEZE = 52, FRICTION = 17, GET_HELD_ENTITY = 22, + HIDE_HUD = 41, INIT = 36, + INITIALIZE = 75, IS_IN_LIQUID = 12, + IS_ON_FIRE = 45, + IS_UNLOCKED = 45, KILL = 3, LEDGE_GRAB = 31, + LIGHT_LEVEL = 44, + LIGHT_ON_FIRE = 53, ON_COLLISION1 = 4, ON_COLLISION2 = 26, - PROCESS_INPUT = 77, + ON_HIT = 49, + PICKED_UP = 80, + PICKED_UP_BY = 68, + PICK_UP = 67, + PROCESS_INPUT = 78, + SET_CURSED = 54, SET_INVISIBLE = 15, + STANDING_ON = 60, + STOMPED_BY = 61, + STOMP_DAMAGE = 43, STOOD_ON = 32, + STUN = 51, + THROWN_BY = 62, TRIGGER_ACTION = 24, UPDATE_STATE_MACHINE = 2, WALKED_OFF = 30, - WALKED_ON = 29 + WALKED_ON = 29, + WEB_COLLISION = 55 } ---@alias ENTITY_OVERRIDE integer ENT_FLAG = { @@ -8329,6 +8385,137 @@ JUNGLESISTERS = { WARNING_ONE_WAY_DOOR = 4 } ---@alias JUNGLESISTERS integer +KEY = { + ["0"] = 48, + ["1"] = 49, + ["2"] = 50, + ["3"] = 51, + ["4"] = 52, + ["5"] = 53, + ["6"] = 54, + ["7"] = 55, + ["8"] = 56, + A = 65, + ADD = 107, + ALT = 18, + B = 66, + BACKSPACE = 8, + C = 67, + CAPS = 20, + CLEAR = 12, + COMMA = 188, + CTRL = 17, + D = 68, + DECIMAL = 110, + DELETE = 46, + DIVIDE = 111, + DOWN = 40, + E = 69, + END = 35, + ESCAPE = 27, + EXECUTE = 43, + F = 70, + F1 = 112, + F2 = 113, + F3 = 114, + F4 = 115, + F5 = 116, + F6 = 117, + F7 = 118, + F8 = 119, + F9 = 120, + F10 = 121, + F11 = 122, + F12 = 123, + F13 = 124, + F14 = 125, + F15 = 126, + F16 = 127, + F17 = 128, + F18 = 129, + F19 = 130, + F20 = 131, + F21 = 132, + F22 = 133, + F23 = 134, + F24 = 135, + G = 71, + H = 72, + HOME = 36, + I = 73, + INSERT = 45, + J = 74, + K = 75, + L = 76, + LALT = 164, + LCONTROL = 162, + LEFT = 37, + LSHIFT = 160, + M = 77, + MINUS = 189, + MULTIPLY = 106, + N = 78, + NUMPAD0 = 96, + NUMPAD1 = 97, + NUMPAD2 = 98, + NUMPAD3 = 99, + NUMPAD4 = 100, + NUMPAD5 = 101, + NUMPAD6 = 102, + NUMPAD7 = 103, + NUMPAD8 = 104, + NUMPAD9 = 105, + O = 79, + OEM_1 = 186, + OEM_2 = 191, + OEM_3 = 192, + OEM_4 = 219, + OEM_5 = 220, + OEM_6 = 221, + OEM_7 = 222, + OEM_8 = 223, + OEM_102 = 226, + OL_MOD_ALT = 2048, + OL_MOD_CTRL = 256, + OL_MOD_SHIFT = 512, + OL_MOUSE_1 = 1025, + OL_MOUSE_2 = 1026, + OL_MOUSE_3 = 1027, + OL_MOUSE_4 = 1028, + OL_MOUSE_5 = 1029, + OL_MOUSE_WHEEL_DOWN = 1041, + OL_MOUSE_WHEEL_UP = 1042, + P = 80, + PAUSE = 19, + PERIOD = 190, + PGDN = 34, + PGUP = 33, + PLUS = 187, + PRINT = 42, + Q = 81, + R = 82, + RALT = 165, + RCONTROL = 163, + RETURN = 13, + RIGHT = 39, + RSHIFT = 161, + S = 83, + SELECT = 41, + SEPARATOR = 108, + SHIFT = 16, + SNAPSHOT = 44, + SPACE = 32, + SUBTRACT = 109, + T = 84, + TAB = 9, + U = 85, + UP = 38, + V = 86, + W = 87, + X = 88, + Y = 89 +} +---@alias KEY integer LAYER = { BACK = 1, BOTH = -128, @@ -8368,6 +8555,37 @@ LIQUID_POOL = { WATER = 1 } ---@alias LIQUID_POOL integer +LOGIC = { + APEP = 15, + ARENA_1 = 23, + ARENA_2 = 24, + ARENA_3 = 25, + ARENA_ALIEN_BLAST = 26, + ARENA_LOOSE_BOMBS = 27, + BLACK_MARKET = 21, + BUBBLER = 18, + COG_SACRIFICE = 16, + DICESHOP = 6, + DISCOVERY_INFO = 20, + DUAT_BOSSES = 17, + GHOST = 3, + GHOST_TOAST = 4, + JELLYFISH = 22, + MAGMAMAN_SPAWN = 11, + MOON_CHALLENGE = 8, + OLMEC_CUTSCENE = 13, + OUROBOROS = 1, + PLEASURE_PALACE = 19, + PRE_CHALLENGE = 7, + SPEEDRUN = 2, + STAR_CHALLENGE = 9, + SUN_CHALLENGE = 10, + TIAMAT_CUTSCENE = 14, + TUN_AGGRO = 5, + TUTORIAL = 0, + WATER_BUBBLES = 12 +} +---@alias LOGIC integer MASK = { ACTIVEFLOOR = 128, ANY = 0, @@ -8416,6 +8634,10 @@ ON = { ONLINE_LOBBY = 29, OPTIONS = 5, PLAYER_PROFILE = 6, + POST_LAYER_CREATION = 148, + POST_LAYER_DESTRUCTION = 152, + POST_LEVEL_CREATION = 146, + POST_LEVEL_DESTRUCTION = 150, POST_LEVEL_GENERATION = 112, POST_LOAD_JOURNAL_CHAPTER = 139, POST_LOAD_SCREEN = 136, @@ -8424,6 +8646,10 @@ ON = { PRE_GET_FEAT = 140, PRE_GET_RANDOM_ROOM = 113, PRE_HANDLE_ROOM_TILES = 114, + PRE_LAYER_CREATION = 147, + PRE_LAYER_DESTRUCTION = 151, + PRE_LEVEL_CREATION = 145, + PRE_LEVEL_DESTRUCTION = 149, PRE_LEVEL_GENERATION = 110, PRE_LOAD_JOURNAL_CHAPTER = 138, PRE_LOAD_LEVEL_FILES = 109, @@ -8849,6 +9075,12 @@ QUEST_FLAG = { WADDLER_AGGROED = 10 } ---@alias QUEST_FLAG integer +RECURSIVE_MODE = { + EXCLUSIVE = 0, + INCLUSIVE = 1, + NONE = 2 +} +---@alias RECURSIVE_MODE integer RENDER_INFO_OVERRIDE = { DTOR = 0, RENDER = 3 @@ -9727,7 +9959,11 @@ TILE_CODE = { APEP = 379, APEP_LEFT = 380, APEP_RIGHT = 381, + ARROW_METAL = 393, + ARROW_METAL_POISON = 395, ARROW_TRAP = 55, + ARROW_WOODEN = 392, + ARROW_WOODEN_POISON = 394, ASSASSIN = 254, AUTOWALLTORCH = 100, BABYLON_FLOOR = 18, @@ -9747,7 +9983,7 @@ TILE_CODE = { BOMB_BOX = 284, BONE_BLOCK = 6, BONE_KEY = 305, - BOOMBOX = 392, + BOOMBOX = 397, BOOMERANG = 318, BOULDER = 378, BUBBLE_PLATFORM = 369, @@ -9830,7 +10066,7 @@ TILE_CODE = { EGGPLANT_CHILD = 159, EGGPLANT_CROWN = 302, EGGPLANT_DOOR = 179, - EGGPLUP = 407, + EGGPLUP = 412, EGGSAC = 344, EGGSAC_BOTTOM = 348, EGGSAC_LEFT = 345, @@ -9858,19 +10094,19 @@ TILE_CODE = { FORCEFIELD = 217, FORCEFIELD_HORIZONTAL = 333, FORCEFIELD_HORIZONTAL_TOP = 334, - FORCEFIELD_TIMED = 411, + FORCEFIELD_TIMED = 416, FORCEFIELD_TOP = 70, FOUNTAIN_DRAIN = 143, FOUNTAIN_HEAD = 142, FROG = 259, FROG_ORANGE = 260, - FURNITURE_CHAIR_LOOKING_LEFT = 400, - FURNITURE_CHAIR_LOOKING_RIGHT = 401, - FURNITURE_CONSTRUCTION_SIGN = 397, - FURNITURE_DININGTABLE = 402, - FURNITURE_DRESSER = 393, - FURNITURE_SIDETABLE = 404, - FURNITURE_SINGLEBED = 398, + FURNITURE_CHAIR_LOOKING_LEFT = 405, + FURNITURE_CHAIR_LOOKING_RIGHT = 406, + FURNITURE_CONSTRUCTION_SIGN = 402, + FURNITURE_DININGTABLE = 407, + FURNITURE_DRESSER = 398, + FURNITURE_SIDETABLE = 409, + FURNITURE_SINGLEBED = 403, GHIST_DOOR2 = 48, GHIST_PRESENT = 358, GHIST_SHOPKEEPER = 220, @@ -9903,7 +10139,7 @@ TILE_CODE = { HOUYIBOW = 205, HUMPHEAD = 331, HUNDUN = 261, - HUNDUN_SPIKES = 403, + HUNDUN_SPIKES = 408, ICEFLOOR = 152, IDOL = 186, IDOL_FLOOR = 187, @@ -9953,7 +10189,7 @@ TILE_CODE = { MOUNT_AXOLOTL = 329, MOUNT_QILIN = 330, MOUNT_ROCKDOG = 328, - MOVABLE_SPIKES = 391, + MOVABLE_SPIKES = 396, MUMMY = 133, MUSHROOM_BASE = 103, NECROMANCER = 136, @@ -9978,7 +10214,7 @@ TILE_CODE = { PALACE_SIGN = 359, PALACE_TABLE = 167, PALACE_TABLE_TRAY = 168, - PANGXIE = 410, + PANGXIE = 415, PARACHUTE = 297, PASTE = 294, PEN_FLOOR = 224, @@ -9995,9 +10231,9 @@ TILE_CODE = { POT = 108, POTOFGOLD = 90, POWDER_KEG = 54, - POWDER_KEG_TIMED = 412, + POWDER_KEG_TIMED = 417, PRESENT = 332, - PROTO_GENERATOR = 406, + PROTO_GENERATOR = 411, PROTO_SHOPKEEPER = 355, PUNISHBALL = 370, PUNISHBALL_ATTACH = 371, @@ -10007,7 +10243,7 @@ TILE_CODE = { PUNISHBALL_ATTACH_TOP = 387, PUSH_BLOCK = 53, QUICKSAND = 66, - QUILLBACK = 396, + QUILLBACK = 401, REDSKELETON = 244, REGENERATING_BLOCK = 31, ROBOT = 123, @@ -10040,7 +10276,7 @@ TILE_CODE = { SINGLEBED = 73, SISTER = 226, SKELETON = 243, - SKELETON_KEY = 395, + SKELETON_KEY = 400, SKULL = 390, SKULL_DROP_TRAP = 353, SLEEPING_HIREDHAND = 222, @@ -10051,7 +10287,7 @@ TILE_CODE = { SNAP_TRAP = 112, SORCERESS = 134, SPARK_TRAP = 163, - SPARROW = 405, + SPARROW = 410, SPECS = 289, SPIDER = 350, SPIDER_FALLING = 351, @@ -10060,7 +10296,7 @@ TILE_CODE = { SPIKEBALL_NO_BOUNCE = 377, SPIKEBALL_TRAP = 376, SPIKES = 51, - SPIKES_UPSIDEDOWN = 413, + SPIKES_UPSIDEDOWN = 418, SPRING_TRAP = 151, STARTING_EXIT = 44, STICKY_TRAP = 177, @@ -10078,7 +10314,7 @@ TILE_CODE = { TEMPLE_FLOOR = 16, THIEF = 228, THINICE = 153, - THIN_ICE = 394, + THIN_ICE = 399, THORN_VINE = 56, TIAMAT = 172, TIKIMAN = 118, @@ -10091,12 +10327,12 @@ TILE_CODE = { TREASURE_VAULTCHEST = 89, TREE_BASE = 102, TRUE_CROWN = 303, - TUN = 408, + TUN = 413, TURKEY = 104, TUTORIAL_MENU_SIGN = 274, TUTORIAL_SPEEDRUN_SIGN = 273, TV = 81, - UDJAT_CHEST = 399, + UDJAT_CHEST = 404, UDJAT_EYE = 298, UDJAT_KEY = 272, UDJAT_SOCKET = 194, @@ -10106,8 +10342,9 @@ TILE_CODE = { USHABTI = 71, VAMPIRE = 249, VAMPIRE_FLYING = 250, - VAN_HORSING = 409, + VAN_HORSING = 414, VAULT_WALL = 195, + VENOM = 391, VINE = 34, VLAD = 126, VLADS_CAPE = 308, @@ -10807,3 +11044,4 @@ local MAX_PLAYERS = 4 ---@alias SHORT_TILE_CODE integer; ---@alias STRINGID integer; ---@alias FEAT integer; +---@alias KEY integer; diff --git a/docs/game_data/tile_codes.txt b/docs/game_data/tile_codes.txt index d6481afec..c30223c7e 100644 --- a/docs/game_data/tile_codes.txt +++ b/docs/game_data/tile_codes.txt @@ -5,7 +5,7 @@ FIREBUG: 248 PALACE_CHANDELIER: 169 BONE_BLOCK: 6 DOOR2: 45 -PANGXIE: 410 +PANGXIE: 415 NONREPLACEABLE_FLOOR: 5 APEP: 379 GHIST_SHOPKEEPER: 220 @@ -44,7 +44,7 @@ GROWABLE_VINE: 35 USHABTI: 71 BABYLON_FLOOR: 18 VLAD_FLOOR: 22 -VAN_HORSING: 409 +VAN_HORSING: 414 STONE_FLOOR: 15 SPIKEBALL_TRAP: 376 MOUNT_AXOLOTL: 329 @@ -52,7 +52,7 @@ PAGODA_FLOOR: 17 BEE: 257 NONREPLACEABLE_BABYLON_FLOOR: 19 BEEHIVE_FLOOR: 21 -QUILLBACK: 396 +QUILLBACK: 401 EXIT: 42 COG_FLOOR: 23 MOTHERSHIP_FLOOR: 24 @@ -139,7 +139,7 @@ TREASURE: 87 SHOP_WALL: 210 TREASURE_CHEST: 88 GOLDBARS: 91 -FORCEFIELD_TIMED: 411 +FORCEFIELD_TIMED: 416 CRATE: 92 CRATE_BOMBS: 93 BACK_HOVERPACK: 311 @@ -168,6 +168,7 @@ CAVEMAN: 114 CAVEMAN_ASLEEP: 115 SCORPION: 116 CAVEMANBOSS: 117 +ARROW_METAL: 393 BODYGUARD: 230 TIKIMAN: 118 WITCHDOCTOR: 119 @@ -196,11 +197,12 @@ JIANGSHI: 138 EXCALIBUR_STONE: 198 OCTOPUS: 139 HERMITCRAB: 140 -PROTO_GENERATOR: 406 +PROTO_GENERATOR: 411 PUNISHBALL_ATTACH: 371 GIANTCLAM: 141 FOUNTAIN_HEAD: 142 FOUNTAIN_DRAIN: 143 +ARROW_WOODEN: 392 KINGU: 144 SLIDINGWALL_CEILING: 146 ALIEN: 148 @@ -244,7 +246,7 @@ EGGSAC_TOP: 346 MOTHER_STATUE: 178 VLAD_FLYING: 251 EGGPLANT_DOOR: 179 -FURNITURE_SINGLEBED: 398 +FURNITURE_SINGLEBED: 403 GIANT_FROG: 180 JUMPDOG: 181 MINISTER: 182 @@ -307,7 +309,7 @@ FROG: 259 FROG_ORANGE: 260 GHOST: 264 GHOST_MED_HAPPY: 266 -POWDER_KEG_TIMED: 412 +POWDER_KEG_TIMED: 417 GHOST_SMALL_ANGRY: 267 OLMITE_NAKED: 382 GHOST_SMALL_SAD: 268 @@ -327,10 +329,10 @@ ROPE: 281 ROPE_PILE: 282 GUN_SHOTGUN: 314 BOMB_BAG: 283 -FURNITURE_DININGTABLE: 402 +FURNITURE_DININGTABLE: 407 BOMB: 285 SEEDED_RUN_UNLOCKER: 288 -HUNDUN_SPIKES: 403 +HUNDUN_SPIKES: 408 SPECS: 289 PITCHERS_MITT: 291 UDJAT_TARGET: 327 @@ -354,7 +356,7 @@ BACK_POWERPACK: 312 SLIDINGWALL: 375 TELEPORTER: 317 MACHETE: 319 -TUN: 408 +TUN: 413 EXCALIBUR: 320 EXCALIBUR_BROKEN: 321 SHIELD_WOODEN: 325 @@ -363,7 +365,7 @@ SPIKEBALL: 342 MOUNT_QILIN: 330 HUMPHEAD: 331 PRESENT: 332 -FURNITURE_CONSTRUCTION_SIGN: 397 +FURNITURE_CONSTRUCTION_SIGN: 402 FORCEFIELD_HORIZONTAL_TOP: 334 ROPE_UNROLLED: 338 COSMIC_ORB: 339 @@ -388,24 +390,27 @@ CRITTER_CRAB: 364 CRITTER_LOCUST: 365 CRITTER_PENGUIN: 366 CRITTER_DRONE: 368 -SPARROW: 405 +SPARROW: 410 BUBBLE_PLATFORM: 369 PUNISHBALL: 370 GIANT_FLY: 372 -SPIKES_UPSIDEDOWN: 413 +SPIKES_UPSIDEDOWN: 418 FLYING_FISH: 373 CRABMAN: 374 OLMITE_ARMORED: 384 PUNISHBALL_ATTACH_RIGHT: 386 CRITTER_SLIME: 389 +VENOM: 391 SKULL: 390 -MOVABLE_SPIKES: 391 -BOOMBOX: 392 -FURNITURE_DRESSER: 393 -SKELETON_KEY: 395 -THIN_ICE: 394 -UDJAT_CHEST: 399 -FURNITURE_CHAIR_LOOKING_LEFT: 400 -FURNITURE_CHAIR_LOOKING_RIGHT: 401 -FURNITURE_SIDETABLE: 404 -EGGPLUP: 407 +ARROW_WOODEN_POISON: 394 +ARROW_METAL_POISON: 395 +MOVABLE_SPIKES: 396 +BOOMBOX: 397 +FURNITURE_DRESSER: 398 +SKELETON_KEY: 400 +THIN_ICE: 399 +UDJAT_CHEST: 404 +FURNITURE_CHAIR_LOOKING_LEFT: 405 +FURNITURE_CHAIR_LOOKING_RIGHT: 406 +FURNITURE_SIDETABLE: 409 +EGGPLUP: 412 diff --git a/docs/game_data/vtable_sizes.csv b/docs/game_data/vtable_sizes.csv index 0ca8e1b9f..5ddfa2499 100644 --- a/docs/game_data/vtable_sizes.csv +++ b/docs/game_data/vtable_sizes.csv @@ -4,7 +4,7 @@ TypeID,Name,vtable offset,~Entity,create_rendering_info,handle_state_machine,kil 3,ENT_TYPE_FLOOR_BORDERTILE_OCTOPUS,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 4,ENT_TYPE_FLOOR_GENERIC,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 5,ENT_TYPE_FLOOR_SURFACE,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 -6,ENT_TYPE_FLOOR_SURFACE_HIDDEN,48206,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,ret,0x2287cbd0,0x2287d100,ret 0,ret true,ret,ret,ret,ret,0x228bff80,0x2287d100,ret 0,0x228bfe80,ret,ret,0x228bfe90,0x2292e2c0,0x228bdfb0,ret true,ret 0,ret 0,ret,ret,0x228ee610,0x22997220,0x228bdfb0,0x228c4340,ret 0,ret 0,0x22999730,0x22999750,0x22999aa0,0x228c6ab0,0x22999f90,ret true,ret 0,ret 0,0x22999fc0,ret,0x2299a010,0x228c6ab0,0x228bdfb0,0x2289e280,0x2299bf40,0x2299d580,0x2299d8a0,0x229a3840,0x229a3b00,0xffff817e590b0173,0xffff817f590b0000,0x228a9670 +6,ENT_TYPE_FLOOR_SURFACE_HIDDEN,48206,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,ret,0x2287cbd0,0x2287d100,ret 0,ret true,ret,ret,ret,ret,0x228bff80,0x2287d100,ret 0,0x228bfe80,ret,ret,0x228bfe90,0x2292e2c0,0x228bdfb0,ret true,ret 0,ret 0,ret,ret,0x228ee610,0x22997220,0x228bdfb0,0x228c4340,ret 0,ret 0,0x22999730,0x22999750,0x22999aa0,0x228c6ab0,0x22999f90,ret true,ret 0,ret 0,0x22999fc0,ret,0x2299a010,0x228c6ab0,0x228bdfb0,0x2289e280,0x2299bf40,0x2299d580,0x2299d8a0,0x229a3840,0x229a3b00,0xffff817e87880173,0xffff817f87880000,0x228a9670 7,ENT_TYPE_FLOOR_BASECAMP_SINGLEBED,10626,0x2287aed0,0x227fcd90,ret,0x2297d3f0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x2297d600,0x2297d680,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 8,ENT_TYPE_FLOOR_BASECAMP_DININGTABLE,10626,0x2287aed0,0x227fcd90,ret,0x2297d3f0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x2297d600,0x2297d680,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 9,ENT_TYPE_FLOOR_BASECAMP_LONGTABLE,10626,0x2287aed0,0x227fcd90,ret,0x2297d3f0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x2297d600,0x2297d680,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 @@ -91,7 +91,7 @@ TypeID,Name,vtable offset,~Entity,create_rendering_info,handle_state_machine,kil 90,ENT_TYPE_FLOOR_FORCEFIELD_TOP,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 91,ENT_TYPE_FLOOR_HORIZONTAL_FORCEFIELD,8946,0x2287aed0,0x227fcd90,0x22974270,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x22974960,0x227fd790,0x227fd930,0x227fd950,0x22974760,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22974240,END OF ENTITY,0x22928d00,ret,0x2287cbd0 92,ENT_TYPE_FLOOR_HORIZONTAL_FORCEFIELD_TOP,8786,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 -93,ENT_TYPE_FLOOR_PEN,48142,0x2287aed0,0x227fcd90,ret,0x22978910,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x22978990,0x90980155e11000c,0x680115c0e030c,0x6801159150009,0x7801159150009,0x9801359130008,0x980135915000a,0xa80135916000b,0xbf7f80095916000b,0xffff8009988b0000 +93,ENT_TYPE_FLOOR_PEN,48142,0x2287aed0,0x227fcd90,ret,0x22978910,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x22978990,0x90980158c8e000c,0x680118a8b030c,0x6801187920009,0x7801187920009,0x9801387900008,0x980138792000a,0xa80138793000b,0xbf7f80098793000b,0xffff8009c7080000 94,ENT_TYPE_FLOOR_TOMB,8906,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x2298cc30,END OF ENTITY,0x22928d00,0x2292c410,0x2288b9e0 95,ENT_TYPE_FLOOR_YAMA_PLATFORM,8866,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x2288d9c0,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2287cbd0 96,ENT_TYPE_FLOOR_EMPRESS_GRAVE,8826,0x2287aed0,0x227fcd90,ret,0x22926290,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x22927b90,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,0x22927f40,ret,ret,0x22928420,ret,ret,0x227fe2f0,0x22928bd0,END OF ENTITY,0x22928d00,0x2292c410,0x2288b9e0 @@ -811,7 +811,7 @@ TypeID,Name,vtable offset,~Entity,create_rendering_info,handle_state_machine,kil 841,ENT_TYPE_BG_EGGSAC_STAINS,2460,0x2287aed0,0x227fcd90,ret,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY 844,ENT_TYPE_LOGICAL_CONSTELLATION,2840,0x2287aed0,0x227fcd90,ret,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x229f53f0,END OF ENTITY 845,ENT_TYPE_LOGICAL_SHOOTING_STARS_SPAWNER,2802,0x2287aed0,0x227fcd90,0x22a004c0,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x22a004b0,END OF ENTITY -846,ENT_TYPE_LOGICAL_DOOR,48710,0x2287aed0,0x227fcd90,0x229f8090,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY,0xffff8009988b0000,0x403f8009990b0000 +846,ENT_TYPE_LOGICAL_DOOR,48710,0x2287aed0,0x227fcd90,0x229f8090,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY,0xffff8009c7080000,0x403f8009c7880000 847,ENT_TYPE_LOGICAL_DOOR_AMBIENT_SOUND,2764,0x2287aed0,0x227fcd90,0x229f83c0,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x229f8360,END OF ENTITY 848,ENT_TYPE_LOGICAL_BLACKMARKET_DOOR,2726,0x2287aed0,0x227fcd90,0x229f8090,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,ret,ret 0,ret,ret,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,0x229f3af0,END OF ENTITY 849,ENT_TYPE_LOGICAL_ARROW_TRAP_TRIGGER,48672,0x2287aed0,0x227fcd90,0x229f35f0,0x227fcfd0,ret,0x227fd000,0x227fd0b0,0x227fd110,0x227fd150,0x227fd570,0x227fd580,ret 0,0x227fd5a0,0x227fd730,0x227fd740,0x227fd750,0x227fd770,0x227fd790,0x227fd930,0x227fd950,0x227fd960,0x227fe160,ret 0,0x229f3a20,ret 0,ret,0x229f3a40,ret 0,ret,ret,ret,ret,ret,ret,ret,0x227fe2f0,ret,END OF ENTITY diff --git a/docs/generate.py b/docs/generate.py index ac37baa95..3cb7c047c 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -9,55 +9,7 @@ if not os.path.exists("src/includes"): os.makedirs("src/includes") -# if you change anything in here, please update it in validator.py as well -replace_table = { - # standard basic types - "uint8_t": "int", - "uint16_t": "int", - "uint32_t": "int", - "uint64_t": "int", - "int8_t": "int", - "int16_t": "int", - "int32_t": "int", - "int64_t": "int", - "ImU32": "int", - "in_port_t": "int", - "size_t": "int", - "char*": "string", - "wstring": "string", - "u16string": "string", - "string_view": "string", - "char16_t*": "string", - "char16_t": "char", - "pair<": "tuple<", - # std containers - "custom_vector<": "vector<", - "custom_map<": "map<", - "custom_unordered_map<": "map<", - "custom_set<": "set<", - "custom_unordered_set<": "set<", - "game_vector<": "vector<", - "game_map<": "map<", - "game_unordered_map<": "map<", - "game_set<": "set<", - "game_unordered_set<": "set<", - "unordered_map<": "map<", # doesn't seam to matter for lua if it's ordered or not - "unordered_set<": "set<", # doesn't seam to matter for lua if it's ordered or not - # removers - ", identity_hasher<>": "", - "std::": "", - "sol::": "", - "void": "", - "constexpr": "", - "const": "", - "static": "", - # special - "variadic_args va": "ENT_TYPE, ENT_TYPE...", - "EmittedParticlesInfo": "array", - "ImVec2": "Vec2", - "SoundCallbackFunction": "function", - "object ": "any ", -} +replace_table = ps.replace_table def replace_all(text): for repl, wth in replace_table.items(): diff --git a/docs/generate_emmylua.py b/docs/generate_emmylua.py index 25eab9678..ac64ac002 100644 --- a/docs/generate_emmylua.py +++ b/docs/generate_emmylua.py @@ -35,16 +35,17 @@ "...va:": "...ent_type:", "set<": "Array<", "span<": "Array<", - + "game_table": "table", "custom_table": "table", "game_Array": "Array", "custom_Array": "Array", - + "const ": "", "EmittedParticlesInfo": "Array", "object": "any", "ImVec2": "Vec2", + "BucketItem": "any", } reFloat = re.compile(r"\bfloat\b") diff --git a/docs/parse_source.py b/docs/parse_source.py index b1d8b4336..ed717c2f0 100644 --- a/docs/parse_source.py +++ b/docs/parse_source.py @@ -11,6 +11,57 @@ if not os.path.exists(".db"): os.makedirs(".db") +# this is common for generator.py and validator.py, not actually used nor should it be used here +replace_table = { + # standard basic types + "uint8_t": "int", + "uint16_t": "int", + "uint32_t": "int", + "uint64_t": "int", + "int8_t": "int", + "int16_t": "int", + "int32_t": "int", + "int64_t": "int", + "ImU32": "int", + "in_port_t": "int", + "size_t": "int", + "char*": "string", + "wstring": "string", + "u16string": "string", + "string_view": "string", + "char16_t*": "string", + "char16_t": "char", + "pair<": "tuple<", + # std containers + "custom_vector<": "vector<", + "custom_map<": "map<", + "custom_unordered_map<": "map<", + "custom_set<": "set<", + "custom_unordered_set<": "set<", + "game_vector<": "vector<", + "game_map<": "map<", + "game_unordered_map<": "map<", + "game_set<": "set<", + "game_unordered_set<": "set<", + "unordered_map<": "map<", # doesn't seam to matter for lua if it's ordered or not + "unordered_set<": "set<", # doesn't seam to matter for lua if it's ordered or not + # removers + ", identity_hasher<>": "", + "std::": "", + "sol::": "", + "void": "", + "constexpr": "", + "const": "", + "static": "", + # special + "variadic_args va": "ENT_TYPE, ENT_TYPE...", + "EmittedParticlesInfo": "array", + "ImVec2": "Vec2", + "SoundCallbackFunction": "function", + "object ": "any ", + "BucketItem": "any", +} + header_files = [ "../src/game_api/math.hpp", "../src/game_api/rpc.hpp", @@ -64,6 +115,7 @@ "../src/game_api/script/usertypes/gui_lua.cpp", "../src/game_api/steam_api.hpp", "../src/game_api/search.hpp", + "../src/game_api/bucket.hpp", ] api_files = [ "../src/game_api/script/script_impl.cpp", @@ -107,6 +159,7 @@ "../src/game_api/script/usertypes/socket_lua.cpp", "../src/game_api/script/usertypes/steam_lua.cpp", "../src/game_api/script/usertypes/logic_lua.cpp", + "../src/game_api/script/usertypes/bucket_lua.cpp", ] vtable_api_files = [ "../src/game_api/script/usertypes/vtables_lua.cpp", @@ -170,8 +223,8 @@ def camel_case_to_snake_case(name): def fix_spaces(thing): thing = thing.strip() - thing = re.sub("\s{2,}", " ", thing) # change double spaces into single - thing = re.sub("(?<=\(|\<)\s", "", thing) # remove spaces after ( or < + thing = re.sub(r"\s{2,}", " ", thing) # change double spaces into single + thing = re.sub(r"(?<=\(|\<)\s", "", thing) # remove spaces after ( or < return thing.replace("*", "").replace("&", "") # remove * and & def getfunc(name): @@ -413,6 +466,9 @@ def run_parse(): (item for item in classes if item["name"] == cpp_type), dict() ) + if "member_funs" not in underlying_cpp_type: + underlying_cpp_type["member_funs"] = {} + if name not in underlying_cpp_type["member_funs"]: underlying_cpp_type["member_funs"][name] = [] @@ -651,7 +707,7 @@ def run_parse(): if var[0].startswith("sol::constructors"): for fun in underlying_cpp_type["member_funs"][cpp_type]: param = fun["param"] - + if cpp_type not in constructors: constructors[cpp_type] = [] constructors[cpp_type].append( diff --git a/docs/src/includes/_enums.md b/docs/src/includes/_enums.md index 378ca43f0..3396d67a8 100644 --- a/docs/src/includes/_enums.md +++ b/docs/src/includes/_enums.md @@ -465,19 +465,19 @@ Name | Data | Description Name | Data | Description ---- | ---- | ----------- -[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.NONE) | 0 | -[JUMP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JUMP) | 1 | -[WHIP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.WHIP) | 2 | -[BOMB](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.BOMB) | 4 | -[ROPE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.ROPE) | 8 | -[RUN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RUN) | 16 | -[DOOR](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOOR) | 32 | -[MENU](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.MENU) | 64 | -[JOURNAL](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JOURNAL) | 128 | -[LEFT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.LEFT) | 256 | -[RIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RIGHT) | 512 | -[UP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.UP) | 1024 | -[DOWN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOWN) | 2048 | +[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.NONE) | 0x0 | +[JUMP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JUMP) | 0x1 | +[WHIP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.WHIP) | 0x2 | +[BOMB](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.BOMB) | 0x4 | +[ROPE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.ROPE) | 0x8 | +[RUN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RUN) | 0x10 | +[DOOR](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOOR) | 0x20 | +[MENU](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.MENU) | 0x40 | +[JOURNAL](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.JOURNAL) | 0x80 | +[LEFT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.LEFT) | 0x100 | +[RIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.RIGHT) | 0x200 | +[UP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.UP) | 0x400 | +[DOWN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=INPUTS.DOWN) | 0x800 | ## INPUT_FLAG @@ -609,6 +609,17 @@ Name | Data | Description [GREAT_PARTY_HUH](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=JUNGLESISTERS.GREAT_PARTY_HUH) | 5 | [I_WISH_BROUGHT_A_JACKET](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=JUNGLESISTERS.I_WISH_BROUGHT_A_JACKET) | 6 | +## KEY + + +> Search script examples for [KEY](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=KEY) + + +Name | Data | Description +---- | ---- | ----------- +[A](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=KEY.A) | 65 | +...check [lua_enums.txt](game_data/lua_enums.txt)... | | + ## LAYER @@ -748,6 +759,26 @@ Name | Data | Description [LIQUID](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MASK.LIQUID) | 0x6000 | Short for ([MASK](#MASK).WATER | [MASK](#MASK).LAVA)
[ANY](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MASK.ANY) | 0x0 | Value of 0, treated by all the functions as ANY mask
+## MENU_INPUT + + +> Search script examples for [MENU_INPUT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT) + + + +Name | Data | Description +---- | ---- | ----------- +[NONE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.NONE) | 0x0 | +[SELECT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.SELECT) | 0x1 | +[BACK](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.BACK) | 0x2 | +[DELETE](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.DELETE) | 0x4 | +[RANDOM](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.RANDOM) | 0x8 | +[JOURNAL](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.JOURNAL) | 0x10 | +[LEFT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.LEFT) | 0x20 | +[RIGHT](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.RIGHT) | 0x40 | +[UP](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.UP) | 0x80 | +[DOWN](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=MENU_INPUT.DOWN) | 0x100 | + ## ON diff --git a/docs/src/includes/_globals.md b/docs/src/includes/_globals.md index fb4b69e9b..e26bf8c08 100644 --- a/docs/src/includes/_globals.md +++ b/docs/src/includes/_globals.md @@ -1371,6 +1371,15 @@ Force the journal to open on a chapter and entry# when pressing the journal butt Get the current adventure seed pair +### get_bucket + + +> Search script examples for [get_bucket](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_bucket) + +#### [Bucket](#Bucket) get_bucket() + +Returns the [Bucket](#Bucket) of data stored in shared memory between [Overlunky](#Overlunky) and Playlunky + ### get_character_heart_color @@ -1397,7 +1406,7 @@ short for state->money_shop_total + loop[inventory.money + inventory.collected_m #### int get_frame() -Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. +Get the current frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter is paused if you block PRE_UPDATE from running, and also doesn't increment during some loading screens, even though state update still runs. ### get_frametime @@ -1417,6 +1426,15 @@ Get engine target frametime (1/framerate, default 1/60). Get engine target frametime when game is unfocused (1/framerate, default 1/33). +### get_global_frame + + +> Search script examples for [get_global_frame](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_global_frame) + +#### int get_global_frame() + +Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter keeps incrementing when state is updated, even during loading screens. + ### get_hud @@ -1881,6 +1899,15 @@ end, ON.PRE_LOAD_SCREEN) Sets the specified setting temporarily. These values are not saved and might reset to the users real settings if they visit the options menu. (Check example.) All settings are available in unsafe mode and only a smaller subset [SAFE_SETTING](#SAFE_SETTING) by default for [Hud](#Hud) and other visuals. Returns false, if setting failed. +### set_start_level_paused + + +> Search script examples for [set_start_level_paused](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_start_level_paused) + +#### nil set_start_level_paused(bool enable) + +Setting to true will stop the state update from unpausing after a screen load, leaving you with state.pause == [PAUSE](#PAUSE).FADE on the first frame to do what you want. + ### set_storage_layer @@ -2034,7 +2061,7 @@ Returns: [ImGuiIO](#ImGuiIO) for raw keyboard, mouse and xinput gamepad stuff. - Note: The clicked/pressed actions only make sense in `ON.GUIFRAME`. - Note: Lua starts indexing at 1, you need `keysdown[string.byte('A') + 1]` to find the A key. -- Note: Overlunky/etc will eat all keys it is currently configured to use, your script will only get leftovers. +- Note: [Overlunky](#Overlunky)/etc will eat all keys it is currently configured to use, your script will only get leftovers. - Note: [Gamepad](#Gamepad) is basically [XINPUT_GAMEPAD](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad) but variables are renamed and values are normalized to -1.0..1.0 range. ### mouse_position @@ -2046,33 +2073,6 @@ Returns: [ImGuiIO](#ImGuiIO) for raw keyboard, mouse and xinput gamepad stuff. Current mouse cursor position in screen coordinates. -### return_input - - -> Search script examples for [return_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=return_input) - -#### nil return_input(int uid) - -Return input previously stolen with [steal_input](#steal_input) - -### send_input - - -> Search script examples for [send_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=send_input) - -#### nil send_input(int uid, [INPUTS](#INPUTS) buttons) - -Send input to entity, has to be previously stolen with [steal_input](#steal_input) - -### steal_input - - -> Search script examples for [steal_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=steal_input) - -#### nil steal_input(int uid) - -Steal input from a [Player](#Player), HiredHand or [PlayerGhost](#PlayerGhost) - ## Lighting functions @@ -2693,6 +2693,15 @@ Translate a distance of `x` tiles to screen distance to be be used in drawing fu Translate an entity position to screen position to be used in drawing functions +### set_camera_position + + +> Search script examples for [set_camera_position](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_camera_position) + +#### nil set_camera_position(float cx, float cy) + +Sets the absolute current camera position without rubberbanding animation. Ignores camera bounds or currently focused uid, but doesn't clear them. Best used in [ON](#ON).RENDER_PRE_GAME or similar. See [Camera](#Camera) for proper camera handling with bounds and rubberbanding. + ### set_camp_camera_bounds_enabled @@ -2709,7 +2718,16 @@ Enables or disables the default position based camp camera bounds, to set them m #### nil zoom(float level) -Set the zoom level used in levels and shops. 13.5 is the default. +Set the zoom level used in levels and shops. 13.5 is the default, or 12.5 for shops. See zoom_reset. + +### zoom_reset + + +> Search script examples for [zoom_reset](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=zoom_reset) + +#### nil zoom_reset() + +Reset the default zoom levels for all areas and sets current zoom level to 13.5. ## Room functions @@ -3696,15 +3714,6 @@ Use [replace_drop](#replace_drop)([DROP](#DROP).ARROWTRAP_WOODENARROW, new_arrow This function never worked properly as too many places in the game individually check for vlads cape and calculate the blood multiplication `default_multiplier` doesn't do anything due to some changes in last game updates, `vladscape_multiplier` only changes the multiplier to some entities death's blood spit -### set_camera_position - - -> Search script examples for [set_camera_position](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_camera_position) - -#### nil set_camera_position(float cx, float cy) - -this doesn't actually work at all. See State -> [Camera](#Camera) the for proper camera handling - ### setflag @@ -3726,6 +3735,30 @@ this doesn't actually work at all. See State -> [Camera](#Camera) the for proper `nil testflag()`
+### steal_input + + +> Search script examples for [steal_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=steal_input) + +`nil steal_input(int uid)`
+Deprecated because it's a weird old hack that crashes the game. You can modify inputs in many other ways, like editing `state.player_inputs.player_slot_1.buttons_gameplay` in PRE_UPDATE or a `set_pre_process_input` hook. Steal input from a Player, HiredHand or PlayerGhost. + +### return_input + + +> Search script examples for [return_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=return_input) + +`nil return_input(int uid)`
+Return input previously stolen with [steal_input](#steal_input) + +### send_input + + +> Search script examples for [send_input](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=send_input) + +`nil send_input(int uid, INPUTS buttons)`
+Send input to entity, has to be previously stolen with [steal_input](#steal_input) + ### read_input diff --git a/docs/src/includes/_home.md b/docs/src/includes/_home.md index 4efbe5a83..122f45c2d 100644 --- a/docs/src/includes/_home.md +++ b/docs/src/includes/_home.md @@ -162,3 +162,4 @@ uColor | int; SHORT_TILE_CODE | int; STRINGID | int; FEAT | int; +KEY | int; diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index 40963f3e2..4cfb50749 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -596,6 +596,15 @@ Type | Name | Description [BackgroundSound](#BackgroundSound) | [pause_menu](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=pause_menu) | [BackgroundSound](#BackgroundSound) | [death_transition](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=death_transition) | +### Bucket + +Shared memory structure used for Playlunky-[Overlunky](#Overlunky) interoperability + +Type | Name | Description +---- | ---- | ----------- +map<string, any> | [data](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=data) | You can store arbitrary simple values here in Playlunky to be read in on [Overlunky](#Overlunky) script for example. +[Overlunky](#Overlunky) | [overlunky](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=overlunky) | Access [Overlunky](#Overlunky) options here, nil if [Overlunky](#Overlunky) is not loaded. + ### Color @@ -766,6 +775,25 @@ Type | Name | Description int | [get_state_id()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_state_id) | int | [get_state_id()](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=get_state_id) | Get the `state_id` of a behavior, this is the id that needs to be returned from a behavior's
`get_next_state_id` to enter this state, given that the behavior is added to the movable. +### Overlunky + +Used to get and set [Overlunky](#Overlunky) settings in [Bucket](#Bucket) + +Type | Name | Description +---- | ---- | ----------- +map<string, any> | [options](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=options) | Current [Overlunky](#Overlunky) options. Read only. +map<string, any> | [set_options](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_options) | Write some select options here to change [Overlunky](#Overlunky) options. Just use the same keys as in options. +map<string, [KEY](#KEY)> | [keys](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=keys) | Current [Overlunky](#Overlunky) key bindings. Read only. You can use this to bind some custom feature to the same unknown key as currently bound by the user. +set<string> | [ignore_keys](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_keys) | Disable some key bindings in [Overlunky](#Overlunky), whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. +set<[KEY](#KEY)> | [ignore_keycodes](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_keycodes) | Disable keys that may or may not be used by [Overlunky](#Overlunky). You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. +set<string> | [ignore_features](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=ignore_features) | TODO: Disable [Overlunky](#Overlunky) features. Doesn't do anything yet. +int | [selected_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=selected_uid) | Currently selected uid in the entity picker or -1 if nothing is selected. +optional<int> | [set_selected_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_selected_uid) | Set currently selected uid in the entity picker or -1 to clear selection. +vector<int> | [selected_uids](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=selected_uids) | Currently selected uids in the entity finder. +int | [hovered_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=hovered_uid) | Currently hovered entity uid or -1 if nothing is hovered. +optional<int> | [set_selected_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_selected_uid) | Set currently selected uid in the entity picker or -1 to clear selection. +optional<vector<int>> | [set_selected_uids](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=set_selected_uids) | Set currently selected uids in the entity finder. + ### PRNG [PRNG](#PRNG) (short for Pseudo-Random-Number-Generator) holds 10 128bit wide buffers of memory that are mutated on every generation of a random number. @@ -2729,14 +2757,20 @@ Type | Name | Description [JournalUI](#JournalUI) | [journal_ui](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=journal_ui) | [SaveRelated](#SaveRelated) | [save_related](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=save_related) | [BackgroundSound](#BackgroundSound) | [main_menu_music](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=main_menu_music) | +array<int, MAX_PLAYERS> | [buttons_controls](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_controls) | Yet another place to get player inputs, in some format +array<int, MAX_PLAYERS> | [buttons_movement](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_movement) | Yet another place to get player inputs, in some format ### GameProps Type | Name | Description ---- | ---- | ----------- -int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | +int | [buttons](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons) | Might be used for some menu inputs not found in buttons_menu +int | [buttons_extra](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_extra) | Might be used for some menu inputs not found in buttons_menu +[MENU_INPUT](#MENU_INPUT) | [buttons_menu_previous](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_menu_previous) | Previous state of buttons_menu +[MENU_INPUT](#MENU_INPUT) | [buttons_menu](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=buttons_menu) | Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in [ON](#ON).PRE_UPDATE. bool | [game_has_focus](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=game_has_focus) | +int | [modal_open](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=modal_open) | ### Items diff --git a/docs/validator.py b/docs/validator.py index 6dd73b139..e7d4f157d 100644 --- a/docs/validator.py +++ b/docs/validator.py @@ -2,55 +2,7 @@ import parse_source as ps -#should be the same as in generate.py -replace_table = { - # standard basic types - "uint8_t": "int", - "uint16_t": "int", - "uint32_t": "int", - "uint64_t": "int", - "int8_t": "int", - "int16_t": "int", - "int32_t": "int", - "int64_t": "int", - "ImU32": "int", - "in_port_t": "int", - "size_t": "int", - "char*": "string", - "wstring": "string", - "u16string": "string", - "string_view": "string", - "char16_t*": "string", - "char16_t": "char", - "pair<": "tuple<", - # std containers - "custom_vector<": "vector<", - "custom_map<": "map<", - "custom_unordered_map<": "map<", - "custom_set<": "set<", - "custom_unordered_set<": "set<", - "game_vector<": "vector<", - "game_map<": "map<", - "game_unordered_map<": "map<", - "game_set<": "set<", - "game_unordered_set<": "set<", - "unordered_map<": "map<", # doesn't seam to matter for lua if it's ordered or not - "unordered_set<": "set<", # doesn't seam to matter for lua if it's ordered or not - # removers - ", identity_hasher<>": "", - "std::": "", - "sol::": "", - "void": "", - "constexpr": "", - "const": "", - "static": "", - # special - "variadic_args va": "ENT_TYPE, ENT_TYPE...", - "EmittedParticlesInfo": "array", - "ImVec2": "Vec2", - "SoundCallbackFunction": "function", - "object ": "any ", -} +replace_table = ps.replace_table def replace_all(text): for repl, wth in replace_table.items(): @@ -120,7 +72,7 @@ def check_types(ret): ret = m.group(2) or "nil" if m or m2: param = (m or m2).group(1) - + check_types(ret) check_types(param) diff --git a/examples/tas_controls.lua b/examples/tas_controls.lua new file mode 100644 index 000000000..c2f8e7e73 --- /dev/null +++ b/examples/tas_controls.lua @@ -0,0 +1,152 @@ +meta = { + name = "Supplemental TAS controls", + description = [[Examples how to override and recreate core Overlunky features. + +This script works in both Overlunky and Playlunky. When Overlunky is loaded, it will disable all matching OL keybinds to make sure they don't interfere with the substitutes implemented in the mod. + +- (Ctrl+Space) toggle FADE+PRE_UPDATE pause +- (Space) frame advance +- (PgUp/PgDn/Home/End) engine frametime control +- (Ctrl+PgUp/PgDn/Home/End) zoom control +- (Ins) toggle level start auto pause +- (Del) toggle skip fades + +]], + author = "Dregu", + version = "1.0" +} + +control = { + type = PAUSE.FADE, + paused = false, + skip = false, + level = false, + fade = false, + frametime = 1/60, + zoom = 13.5, +} + +override_keys = { + KEY.INSERT, + KEY.DELETE, + KEY.HOME, + KEY.END, + KEY.PGUP, + KEY.PGDN, + KEY.SPACE, +} + +set_global_interval(function() + local ol = get_bucket().overlunky + if ol then + for _, v in pairs(override_keys) do + ol.ignore_keycodes:insert(nil, v) + ol.ignore_keycodes:insert(nil, KEY.OL_MOD_CTRL | v) + end + return false + end +end, 60) + + +set_callback(function() + if state.loading > 0 and control.fade then + set_frametime(0) + elseif state.loading > 0 then + set_frametime(1/60) + else + set_frametime(control.frametime) + end + set_frametime_unfocused(0) + if state.logic.ouroboros and get_zoom_level() > 13.5 then + zoom(13.5) + elseif not state.logic.ouroboros then + zoom(control.zoom) + end + if control.fade then + state.fadevalue = 0 + state.fadeout = 0 + state.fadein = 0 + state.loading_black_screen_timer = 0 + end + if control.level and state.loading == 3 and ((not control.fade and state.fadeout == 1) or control.fade) then + control.paused = true + control.skip = false + state.fadevalue = 0 + state.loading = 0 + end + if control.paused and control.skip then + control.skip = false + state.pause = clr_mask(state.pause, control.type) + return false + elseif control.paused then + state.pause = set_mask(state.pause, control.type) + end + return control.paused and not test_flag(state.pause, PAUSE.MENU) +end, ON.PRE_UPDATE) + +set_callback(function(ctx) + local iio = get_io() + if iio.keyctrl then + if iio.keypressed(KEY.SPACE, false) then + control.paused = not control.paused + if control.paused then + state.pause = set_mask(state.pause, control.type) + else + state.pause = clr_mask(state.pause, control.type) + end + elseif iio.keypressed(KEY.PGUP, true) then + control.zoom = control.zoom - 1 + if control.zoom < 1 then control.zoom = 1 end + zoom(control.zoom) + elseif iio.keypressed(KEY.PGDN, true) then + control.zoom = control.zoom + 1 + zoom(control.zoom) + elseif iio.keypressed(KEY.HOME, false) then + control.zoom = 13.5 + zoom(control.zoom) + elseif iio.keypressed(KEY.END, false) then + control.zoom = 0 + zoom(control.zoom) + end + elseif iio.keypressed(KEY.SPACE, true) and control.paused then + control.skip = true + elseif iio.keypressed(KEY.PGUP, true) then + control.frametime = control.frametime / 1.2 + set_frametime(control.frametime) + elseif iio.keypressed(KEY.PGDN, true) then + control.frametime = control.frametime * 1.2 + if control.frametime > 1 then control.frametime = 1 end + set_frametime(control.frametime) + elseif iio.keypressed(KEY.HOME, false) then + control.frametime = 1/60 + set_frametime() + elseif iio.keypressed(KEY.END, false) then + control.frametime = 1/12 + set_frametime(control.frametime) + elseif iio.keypressed(KEY.INSERT, false) then + control.level = not control.level + elseif iio.keypressed(KEY.DELETE, false) then + control.fade = not control.fade + end +end, ON.GUIFRAME) + +register_option_callback("tas_controls", nil, function(ctx) + local old_paused = control.paused + control.paused = ctx:win_check("Paused", control.paused) + if control.paused ~= old_paused then + if control.paused then + state.pause = set_mask(state.pause, control.type) + else + state.pause = clr_mask(state.pause, control.type) + end + end + control.skip = ctx:win_check("Frame advance", control.skip) + control.level = ctx:win_check("Pause on level start", control.level) + control.fade = ctx:win_check("Skip fades", control.fade) + control.type = ctx:win_input_int("Pause type", control.type) + local fps = ctx:win_drag_float("FPS", control.frametime > 0 and 1 / control.frametime or 0, 0, 240) + ctx:win_inline() if ctx:win_button("Reset##ResetFPS") then fps = 60 end + control.frametime = fps > 0 and 1 / fps or 0 + control.zoom = ctx:win_drag_float("Zoom", control.zoom, 1, 100) + ctx:win_inline() if ctx:win_button("Reset##ResetZoom") then control.zoom = 13.5 end +end) diff --git a/examples/tas_test.lua b/examples/tas_test.lua index 4c1581d0a..3a32da412 100644 --- a/examples/tas_test.lua +++ b/examples/tas_test.lua @@ -1,6 +1,6 @@ meta.name = "TAS test" -meta.version = "WIP" -meta.description = "Simple test for TASing a seeded run. Works from start to finish, although you might have to wait a frame or two at the start of boss levels because of cutscene skip weirdness. The rerecording stuff is very WIP." +meta.version = "VERY OBSOLETE" +meta.description = "Seriously don't use or even read this." meta.author = "Dregu" local seed = 0 diff --git a/examples/tas_tool.lua b/examples/tas_tool.lua index df99ac0b1..16f2cff09 100644 --- a/examples/tas_tool.lua +++ b/examples/tas_tool.lua @@ -1,13 +1,6 @@ meta.name = "TAS Tool" -meta.version = "WIP" -meta.description = [[DISCLAIMER: -- STILL BUGGY AND NOTHING WORKS PERFECTLY -- SAVES MIGHT NOT WORK BECAUSE OF STUPID JSON SERIALIZER -- SAVES WILL PROBABLY BREAK IN A LATER UPDATE BECAUSE OF ^ -- TELEPORT DESYNC FOR UNKNOWN REASONS IN LATER STAGES LIKE TIAMAT IS VERY POSSIBLE -- SHOPPIE AGGRO AND STATUS EFFECTS ETC ARE NOT FULLY IMPLEMENTED ON RERECORD - -This is the Tool for Tool Assisted Speedrunning Spelunky 2, to be used in seeded runs with Overlunky frame advance features. In a TAS, you record your inputs frame by frame, with the ability to rerecord your steps if you screw up. The resulting inputs are then played back in real time to recreate the perfect inhuman run. Using unsafe mode to save and load files at will. (Saves .json files to game root.)]] +meta.version = "OBSOLETE" +meta.description = "Discontinued in favor of TAS Wizard by Cosine. Still a good script example but a bad TAS tool." meta.author = "Dregu" meta.unsafe = true diff --git a/src/game_api/aliases.hpp b/src/game_api/aliases.hpp index e665fc5f3..64dc393cb 100644 --- a/src/game_api/aliases.hpp +++ b/src/game_api/aliases.hpp @@ -10,6 +10,7 @@ using VANILLA_SOUND_PARAM = uint32_t; // NoAlias using VANILLA_SOUND_CALLBACK_TYPE = uint32_t; // NoAlias using TEXTURE = int64_t; // NoAlias using INPUTS = uint16_t; // NoAlias +using MENU_INPUT = uint16_t; // NoAlias using BUTTON = uint8_t; // NoAlias using ENT_TYPE = uint32_t; // NoAlias using ROOM_TEMPLATE = uint16_t; // NoAlias @@ -22,6 +23,7 @@ using WORLD_SHADER = uint8_t; // NoAlias using SHORT_TILE_CODE = uint8_t; using STRINGID = uint32_t; using FEAT = uint8_t; +using KEY = int64_t; inline constexpr uint8_t MAX_PLAYERS = 4; diff --git a/src/game_api/bucket.cpp b/src/game_api/bucket.cpp new file mode 100644 index 000000000..576884360 --- /dev/null +++ b/src/game_api/bucket.cpp @@ -0,0 +1,15 @@ +#include "bucket.hpp" + +#include "containers/game_allocator.hpp" +#include "memory.hpp" + +Bucket* Bucket::get() +{ + static auto bucket_offset = get_address("hundun_door_control") - 12; // another random CCCC with some space for a pointer + auto bucket = reinterpret_cast(memory_read(bucket_offset)); + if ((size_t)bucket != 0xCCCCCCCCCCCCCCCC) + return bucket; + auto new_bucket = new Bucket(); + write_mem_prot(bucket_offset, new_bucket, true); + return new_bucket; +} diff --git a/src/game_api/bucket.hpp b/src/game_api/bucket.hpp new file mode 100644 index 000000000..4babaf4a7 --- /dev/null +++ b/src/game_api/bucket.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include + +using BucketItem = std::variant; +using KEY = int64_t; + +struct Overlunky +{ + /// Current Overlunky options. Read only. + std::map options; + /// Write some select options here to change Overlunky options. Just use the same keys as in options. + std::map set_options; + /// Current Overlunky key bindings. Read only. You can use this to bind some custom feature to the same unknown key as currently bound by the user. + std::map keys; + /// Disable some key bindings in Overlunky, whatever key they are actually bound to. Remember this might not be bound to the default any more, so only use this if you also plan on overriding the current keybinding, or just need to disable some feature and don't care what key it is bound on. + std::unordered_set ignore_keys; + /// Disable keys that may or may not be used by Overlunky. You should probably write the keycodes you need here just in case if you think using OL will interfere with your keybinds. + std::unordered_set ignore_keycodes; + /// TODO: Disable Overlunky features. Doesn't do anything yet. + std::unordered_set ignore_features; + /// Currently selected uid in the entity picker or -1 if nothing is selected. + int32_t selected_uid; + /// Set currently selected uid in the entity picker or -1 to clear selection. + std::optional set_selected_uid; + /// Currently hovered entity uid or -1 if nothing is hovered. + int32_t hovered_uid; + /// Currently selected uids in the entity finder. + std::vector selected_uids; + /// Set currently selected uids in the entity finder. + std::optional> set_selected_uids; +}; + +class Bucket +{ + public: + static Bucket* get(); + + /// You can store arbitrary simple values here in Playlunky to be read in on Overlunky script for example. + std::unordered_map data; + /// Access Overlunky options here, nil if Overlunky is not loaded. + Overlunky* overlunky{nullptr}; + // Used by state.cpp to determine if patches should still be applied. + bool patches_applied{false}; +}; diff --git a/src/game_api/entity.cpp b/src/game_api/entity.cpp index c8bc6fd11..d5785a814 100644 --- a/src/game_api/entity.cpp +++ b/src/game_api/entity.cpp @@ -469,14 +469,7 @@ void Movable::set_position(float to_x, float to_y) rendering_info->y_dupe4 += dy; } if (State::get().ptr()->camera->focused_entity_uid == uid) - { - State::get().ptr()->camera->focus_x += dx; - State::get().ptr()->camera->focus_y += dy; - State::get().ptr()->camera->adjusted_focus_x += dx; - State::get().ptr()->camera->adjusted_focus_y += dy; - State::get().ptr()->camera->calculated_focus_x += dx; - State::get().ptr()->camera->calculated_focus_y += dy; - } + State::get().set_camera_position(dx, dy); } template diff --git a/src/game_api/entity_db.cpp b/src/game_api/entity_db.cpp index 629a682ef..83877ad8f 100644 --- a/src/game_api/entity_db.cpp +++ b/src/game_api/entity_db.cpp @@ -97,7 +97,7 @@ EntityDB* get_type(uint32_t id) // Special case: map_ptr might be 0 if it's not initialized. // This only occurs in list_entities; for others, do not check the pointer // to see if this assumption works. - if (!entity_factory_ptr) + if (!entity_factory_ptr || id > 0x393) return nullptr; return entity_factory_ptr->types + id; diff --git a/src/game_api/flags.hpp b/src/game_api/flags.hpp index d13b09060..ee54ceb8d 100644 --- a/src/game_api/flags.hpp +++ b/src/game_api/flags.hpp @@ -756,3 +756,14 @@ std::array level_chances{ "tallroom", "rewardroom", }; + +std::array renderer_flags1{ + "Floor background shadows", + "Emissive lighting", + "Shockwaves, some shadows", + "unknown", + "unknown", + "unknown", + "Liquid smoothing", + "unknown", +}; diff --git a/src/game_api/game_manager.hpp b/src/game_api/game_manager.hpp index 06295cf72..2bc205643 100644 --- a/src/game_api/game_manager.hpp +++ b/src/game_api/game_manager.hpp @@ -72,16 +72,21 @@ struct BackgroundMusic struct GameProps { + /// Might be used for some menu inputs not found in buttons_menu uint32_t buttons; uint32_t unknown1; uint32_t unknown2; uint32_t unknown3; - uint32_t buttons_dupe; + /// Might be used for some menu inputs not found in buttons_menu + uint32_t buttons_extra; uint32_t unknown4; uint32_t unknown5; uint32_t unknown6; - uint32_t buttons_dupe_but_different; - int8_t unknown8; + /// Previous state of buttons_menu + MENU_INPUT buttons_menu_previous; + /// Inputs used to control all the menus, separate from player inputs. You can probably capture and edit this in ON.PRE_UPDATE. + MENU_INPUT buttons_menu; + int8_t modal_open; bool game_has_focus; bool unknown9; bool unknown10; @@ -92,7 +97,9 @@ struct GameManager { BackgroundMusic* music; SaveRelated* save_related; + /// Yet another place to get player inputs, in some format std::array buttons_controls; + /// Yet another place to get player inputs, in some format std::array buttons_movement; GameProps* game_props; diff --git a/src/game_api/rpc.cpp b/src/game_api/rpc.cpp index 64ba19d26..9ea355044 100644 --- a/src/game_api/rpc.cpp +++ b/src/game_api/rpc.cpp @@ -1852,3 +1852,19 @@ void set_camera_layer_control_enabled(bool enable) write_mem_recoverable("set_camera_layer_control", offset2, get_nop(18), true); } } + +void set_start_level_paused(bool enable) +{ + static size_t offset = 0; + if (offset == 0) + { + offset = get_address("unpause_level"); + } + if (offset != 0) + { + if (enable) + write_mem_recoverable("start_level_paused", offset, get_nop(3), true); + else + recover_mem("start_level_paused"); + } +} diff --git a/src/game_api/rpc.hpp b/src/game_api/rpc.hpp index 6428577d3..0b49dc17f 100644 --- a/src/game_api/rpc.hpp +++ b/src/game_api/rpc.hpp @@ -129,5 +129,6 @@ void destroy_layer(uint8_t layer); void destroy_level(); void create_layer(uint8_t layer); void create_level(); +void set_start_level_paused(bool enable); void set_level_logic_enabled(bool enable); void set_camera_layer_control_enabled(bool enable); diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index c0f89c1fb..6d03569f2 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -64,6 +64,7 @@ #include "strings.hpp" // for change_string #include "thread_utils.hpp" // for OnHeapPointer #include "usertypes/behavior_lua.hpp" // for register_usertypes +#include "usertypes/bucket_lua.hpp" // for register_usertypes #include "usertypes/char_state_lua.hpp" // for register_usertypes #include "usertypes/drops_lua.hpp" // for register_usertypes #include "usertypes/entities_activefloors_lua.hpp" // for register_usertypes @@ -297,6 +298,7 @@ end NSteam::register_usertypes(lua); NVTables::register_usertypes(lua); NLogic::register_usertypes(lua); + NBucket::register_usertypes(lua); StateMemory* main_state = State::get().ptr_main(); @@ -964,9 +966,12 @@ end /// Set level flag 18 on post room generation instead, to properly force every level to dark lua["force_dark_level"] = [](bool g) { State::get().darkmode(g); }; - /// Set the zoom level used in levels and shops. 13.5 is the default. + /// Set the zoom level used in levels and shops. 13.5 is the default, or 12.5 for shops. See zoom_reset. lua["zoom"] = [](float level) { State::get().zoom(level); }; + /// Reset the default zoom levels for all areas and sets current zoom level to 13.5. + lua["zoom_reset"] = []() + { State::get().zoom_reset(); }; /// Pause/unpause the game. /// This is just short for `state.pause == 32`, but that produces an audio bug /// I suggest `state.pause == 2`, but that won't run any callback, `state.pause == 16` will do the same but [set_global_interval](#set_global_interval) will still work @@ -1176,8 +1181,10 @@ end lua["lock_door_at"] = lock_door_at; /// Try to unlock the exit at coordinates lua["unlock_door_at"] = unlock_door_at; - /// Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. + /// Get the current frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter is paused if you block PRE_UPDATE from running, and also doesn't increment during some loading screens, even though state update still runs. lua["get_frame"] = get_frame_count; + /// Get the current global frame count since the game was started. You can use this to make some timers yourself, the engine runs at 60fps. This counter keeps incrementing when state is updated, even during loading screens. + lua["get_global_frame"] = get_global_frame_count; /// Get the current timestamp in milliseconds since the Unix Epoch. lua["get_ms"] = []() { return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); }; @@ -1274,8 +1281,7 @@ end { return State::get_camera_position(); }; - /// Deprecated - /// this doesn't actually work at all. See State -> Camera the for proper camera handling + /// Sets the absolute current camera position without rubberbanding animation. Ignores camera bounds or currently focused uid, but doesn't clear them. Best used in ON.RENDER_PRE_GAME or similar. See Camera for proper camera handling with bounds and rubberbanding. lua["set_camera_position"] = set_camera_position; /// Set the nth bit in a number. This doesn't actually change the variable you pass, it just returns the new value you can use. @@ -1314,7 +1320,8 @@ end lua["get_window_size"] = []() -> std::tuple { return {(int)ImGui::GetIO().DisplaySize.x, (int)ImGui::GetIO().DisplaySize.y}; }; - /// Steal input from a Player, HiredHand or PlayerGhost + /// Deprecated + /// Deprecated because it's a weird old hack that crashes the game. You can modify inputs in many other ways, like editing `state.player_inputs.player_slot_1.buttons_gameplay` in PRE_UPDATE or a `set_pre_process_input` hook. Steal input from a Player, HiredHand or PlayerGhost. lua["steal_input"] = [](int uid) { static const auto player_ghost = to_id("ENT_TYPE_ITEM_PLAYERGHOST"); @@ -1353,6 +1360,7 @@ end backend->script_input[uid] = newinput; } }; + /// Deprecated /// Return input previously stolen with [steal_input](#steal_input) lua["return_input"] = [](int uid) { @@ -1377,6 +1385,7 @@ end } backend->script_input.erase(uid); }; + /// Deprecated /// Send input to entity, has to be previously stolen with [steal_input](#steal_input) lua["send_input"] = [](int uid, INPUTS buttons) { @@ -2163,6 +2172,9 @@ end /// 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_level_logic_enabled; + /// Setting to true will stop the state update from unpausing after a screen load, leaving you with state.pause == PAUSE.FADE on the first frame to do what you want. + lua["set_start_level_paused"] = set_start_level_paused; + /// Converts INPUTS to (x, y, BUTTON) lua["inputs_to_buttons"] = [](INPUTS inputs) -> std::tuple { @@ -2207,7 +2219,9 @@ end /// 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("INPUTS", "NONE", 0x0, "JUMP", 0x1, "WHIP", 0x2, "BOMB", 0x4, "ROPE", 0x8, "RUN", 0x10, "DOOR", 0x20, "MENU", 0x40, "JOURNAL", 0x80, "LEFT", 0x100, "RIGHT", 0x200, "UP", 0x400, "DOWN", 0x800); + + lua.create_named_table("MENU_INPUT", "NONE", 0x0, "SELECT", 0x1, "BACK", 0x2, "DELETE", 0x4, "RANDOM", 0x8, "JOURNAL", 0x10, "LEFT", 0x20, "RIGHT", 0x40, "UP", 0x80, "DOWN", 0x100); lua.create_named_table( "ON", diff --git a/src/game_api/script/usertypes/bucket_lua.cpp b/src/game_api/script/usertypes/bucket_lua.cpp new file mode 100644 index 000000000..5443f78d8 --- /dev/null +++ b/src/game_api/script/usertypes/bucket_lua.cpp @@ -0,0 +1,139 @@ +#include "bucket_lua.hpp" + +#include "bucket.hpp" + +namespace NBucket +{ +void register_usertypes(sol::state& lua) +{ + /// Used to get and set Overlunky settings in Bucket + auto ol_type = lua.new_usertype("Overlunky", sol::no_constructor); + ol_type["options"] = &Overlunky::options; + ol_type["set_options"] = &Overlunky::set_options; + ol_type["keys"] = &Overlunky::keys; + ol_type["ignore_keys"] = &Overlunky::ignore_keys; + ol_type["ignore_keycodes"] = &Overlunky::ignore_keycodes; + ol_type["ignore_features"] = &Overlunky::ignore_features; + ol_type["selected_uid"] = &Overlunky::selected_uid; + ol_type["set_selected_uid"] = &Overlunky::set_selected_uid; + ol_type["selected_uids"] = &Overlunky::selected_uids; + ol_type["hovered_uid"] = &Overlunky::hovered_uid; + ol_type["set_selected_uid"] = &Overlunky::set_selected_uid; + ol_type["set_selected_uids"] = &Overlunky::set_selected_uids; + + /// Shared memory structure used for Playlunky-Overlunky interoperability + auto bucket_type = lua.new_usertype("Bucket", sol::no_constructor); + bucket_type["data"] = &Bucket::data; + bucket_type["overlunky"] = sol::readonly(&Bucket::overlunky); + + /// Returns the Bucket of data stored in shared memory between Overlunky and Playlunky + // lua["get_bucket"] = []() -> Bucket* + lua["get_bucket"] = Bucket::get; + + /// Keycodes to be used in various input functions. Most are standard, but the OL_ prefixed keys are specific to Overlunky::ignore_keycodes. + lua.create_named_table("KEY" + //, "A", 65 + //, "", ...check__[lua_enums.txt]\[game_data/lua_enums.txt\]... + ); + + lua["KEY"]["OL_MOD_CTRL"] = 0x100; + lua["KEY"]["OL_MOD_SHIFT"] = 0x200; + lua["KEY"]["OL_MOD_ALT"] = 0x800; + lua["KEY"]["OL_MOUSE_1"] = 0x401; + lua["KEY"]["OL_MOUSE_2"] = 0x402; + lua["KEY"]["OL_MOUSE_3"] = 0x403; + lua["KEY"]["OL_MOUSE_4"] = 0x404; + lua["KEY"]["OL_MOUSE_5"] = 0x405; + lua["KEY"]["OL_MOUSE_WHEEL_DOWN"] = 0x411; + lua["KEY"]["OL_MOUSE_WHEEL_UP"] = 0x412; + + lua["KEY"]["BACKSPACE"] = 0x08; + lua["KEY"]["TAB"] = 0x09; + lua["KEY"]["CLEAR"] = 0x0C; + lua["KEY"]["RETURN"] = 0x0D; + lua["KEY"]["SHIFT"] = 0x10; + lua["KEY"]["CTRL"] = 0x11; + lua["KEY"]["ALT"] = 0x12; + lua["KEY"]["PAUSE"] = 0x13; + lua["KEY"]["CAPS"] = 0x14; + lua["KEY"]["ESCAPE"] = 0x1B; + lua["KEY"]["SPACE"] = 0x20; + lua["KEY"]["PGUP"] = 0x21; + lua["KEY"]["PGDN"] = 0x22; + lua["KEY"]["END"] = 0x23; + lua["KEY"]["HOME"] = 0x24; + lua["KEY"]["LEFT"] = 0x25; + lua["KEY"]["UP"] = 0x26; + lua["KEY"]["RIGHT"] = 0x27; + lua["KEY"]["DOWN"] = 0x28; + lua["KEY"]["SELECT"] = 0x29; + lua["KEY"]["PRINT"] = 0x2A; + lua["KEY"]["EXECUTE"] = 0x2B; + lua["KEY"]["SNAPSHOT"] = 0x2C; + lua["KEY"]["INSERT"] = 0x2D; + lua["KEY"]["DELETE"] = 0x2E; + lua["KEY"]["NUMPAD0"] = 0x60; + lua["KEY"]["NUMPAD1"] = 0x61; + lua["KEY"]["NUMPAD2"] = 0x62; + lua["KEY"]["NUMPAD3"] = 0x63; + lua["KEY"]["NUMPAD4"] = 0x64; + lua["KEY"]["NUMPAD5"] = 0x65; + lua["KEY"]["NUMPAD6"] = 0x66; + lua["KEY"]["NUMPAD7"] = 0x67; + lua["KEY"]["NUMPAD8"] = 0x68; + lua["KEY"]["NUMPAD9"] = 0x69; + lua["KEY"]["MULTIPLY"] = 0x6A; + lua["KEY"]["ADD"] = 0x6B; + lua["KEY"]["SEPARATOR"] = 0x6C; + lua["KEY"]["SUBTRACT"] = 0x6D; + lua["KEY"]["DECIMAL"] = 0x6E; + lua["KEY"]["DIVIDE"] = 0x6F; + lua["KEY"]["F1"] = 0x70; + lua["KEY"]["F2"] = 0x71; + lua["KEY"]["F3"] = 0x72; + lua["KEY"]["F4"] = 0x73; + lua["KEY"]["F5"] = 0x74; + lua["KEY"]["F6"] = 0x75; + lua["KEY"]["F7"] = 0x76; + lua["KEY"]["F8"] = 0x77; + lua["KEY"]["F9"] = 0x78; + lua["KEY"]["F10"] = 0x79; + lua["KEY"]["F11"] = 0x7A; + lua["KEY"]["F12"] = 0x7B; + lua["KEY"]["F13"] = 0x7C; + lua["KEY"]["F14"] = 0x7D; + lua["KEY"]["F15"] = 0x7E; + lua["KEY"]["F16"] = 0x7F; + lua["KEY"]["F17"] = 0x80; + lua["KEY"]["F18"] = 0x81; + lua["KEY"]["F19"] = 0x82; + lua["KEY"]["F20"] = 0x83; + lua["KEY"]["F21"] = 0x84; + lua["KEY"]["F22"] = 0x85; + lua["KEY"]["F23"] = 0x86; + lua["KEY"]["F24"] = 0x87; + lua["KEY"]["LSHIFT"] = 0xA0; + lua["KEY"]["RSHIFT"] = 0xA1; + lua["KEY"]["LCONTROL"] = 0xA2; + lua["KEY"]["RCONTROL"] = 0xA3; + lua["KEY"]["LALT"] = 0xA4; + lua["KEY"]["RALT"] = 0xA5; + lua["KEY"]["PLUS"] = 0xBB; + lua["KEY"]["COMMA"] = 0xBC; + lua["KEY"]["MINUS"] = 0xBD; + lua["KEY"]["PERIOD"] = 0xBE; + lua["KEY"]["OEM_1"] = 0xBA; + lua["KEY"]["OEM_2"] = 0xBF; + lua["KEY"]["OEM_3"] = 0xC0; + lua["KEY"]["OEM_4"] = 0xDB; + lua["KEY"]["OEM_5"] = 0xDC; + lua["KEY"]["OEM_6"] = 0xDD; + lua["KEY"]["OEM_7"] = 0xDE; + lua["KEY"]["OEM_8"] = 0xDF; + lua["KEY"]["OEM_102"] = 0xE2; + for (char c = '0'; c < '9'; c++) + lua["KEY"][std::string{c}] = (int)c; + for (char c = 'A'; c < 'Z'; c++) + lua["KEY"][std::string{c}] = (int)c; +} +}; // namespace NBucket diff --git a/src/game_api/script/usertypes/bucket_lua.hpp b/src/game_api/script/usertypes/bucket_lua.hpp new file mode 100644 index 000000000..76df1045b --- /dev/null +++ b/src/game_api/script/usertypes/bucket_lua.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace sol +{ +class state; +} // namespace sol + +namespace NBucket +{ +void register_usertypes(sol::state& lua); +}; diff --git a/src/game_api/script/usertypes/game_manager_lua.cpp b/src/game_api/script/usertypes/game_manager_lua.cpp index f16cbee0a..94f189ab2 100644 --- a/src/game_api/script/usertypes/game_manager_lua.cpp +++ b/src/game_api/script/usertypes/game_manager_lua.cpp @@ -70,6 +70,8 @@ void register_usertypes(sol::state& lua) gamemanager_type["journal_ui"] = &GameManager::journal_ui; gamemanager_type["save_related"] = &GameManager::save_related; gamemanager_type["main_menu_music"] = &GameManager::main_menu_music; + gamemanager_type["buttons_controls"] = &GameManager::buttons_controls; + gamemanager_type["buttons_movement"] = &GameManager::buttons_movement; lua.new_usertype( "SaveRelated", @@ -97,7 +99,16 @@ void register_usertypes(sol::state& lua) "GameProps", "buttons", &GameProps::buttons, + "buttons_extra", + &GameProps::buttons_extra, + "buttons_menu_previous", + &GameProps::buttons_menu_previous, + "buttons_menu", + &GameProps::buttons_menu, "game_has_focus", - &GameProps::game_has_focus); + &GameProps::game_has_focus, + "modal_open", + sol::property([](GameProps& gp) + { return gp.modal_open == 0; })); } }; // namespace NGM diff --git a/src/game_api/search.cpp b/src/game_api/search.cpp index 102ac3c3f..4da90ec03 100644 --- a/src/game_api/search.cpp +++ b/src/game_api/search.cpp @@ -1826,6 +1826,15 @@ std::unordered_map g_address_rules{ .at_exe() .function_start(), }, + { + // ^ writes to state.pause on state.loading == 3 + "unpause_level"sv, + PatternCommandBuffer{} + .find_inst("\x44\x0F\x29\xBD\x20\x02\x00\x00"sv) + .find_next_inst("c7 46 14 00 00 00 00 24 fd"_gh) + .offset(9) + .at_exe(), + }, { /* it's a static double, just find something that reads it this+0x08 is clearly some kind of framerate related double, cause it's 60, but don't know what it does diff --git a/src/game_api/state.cpp b/src/game_api/state.cpp index 5873fedc1..55e6dff5a 100644 --- a/src/game_api/state.cpp +++ b/src/game_api/state.cpp @@ -9,6 +9,7 @@ #include // for allocator, operator""sv, operator""s #include // for move +#include "bucket.hpp" // for Bucket #include "containers/custom_allocator.hpp" // #include "entities_chars.hpp" // for Player #include "entity.hpp" // for to_id, Entity, HookWithId, EntityDB @@ -36,6 +37,8 @@ #include "virtual_table.hpp" // for get_virtual_function_address, VTABLE... #include "vtable_hook.hpp" // for hook_vtable +static int64_t global_frame_count{0}; + uint16_t StateMemory::get_correct_ushabti() // returns animation_frame of ushabti { return (correct_ushabti + (correct_ushabti / 10) * 2); @@ -294,11 +297,9 @@ State& State::get() strings_init(); init_state_update_hook(); - auto watermark_offset = get_address("destroy_game_manager") - 8; // pulled this out of a hat, its just a random place with some CCCC hopefully - auto watermark = memory_read(watermark_offset); - if (watermark != 0x4C4F4C4F) + auto bucket = Bucket::get(); + if (!bucket->patches_applied) { - write_mem_prot(watermark_offset, "\x4F\x4C\x4F\x4C", true); DEBUG("Applying patches"); patch_tiamat_kill_crash(); patch_orbs_limit(); @@ -306,6 +307,7 @@ State& State::get() patch_liquid_OOB(); patch_ushabti_error(); patch_entering_closed_door_crash(); + bucket->patches_applied = true; } else { @@ -393,24 +395,29 @@ void State::zoom(float level) } } - const auto level_str = to_le_bytes(level); - static const auto zoom_level = get_address("default_zoom_level"); static const auto zoom_shop = get_address("default_zoom_level_shop"); static const auto zoom_camp = get_address("default_zoom_level_camp"); static const auto zoom_telescope = get_address("default_zoom_level_telescope"); // overwrite the defaults - write_mem_prot(zoom_level, level_str, true); - write_mem_prot(zoom_shop, level_str, true); - write_mem_prot(zoom_camp, level_str, true); - write_mem_prot(zoom_telescope, level_str, true); + write_mem_recoverable("zoom", zoom_level, level, true); + write_mem_recoverable("zoom", zoom_shop, level, true); + write_mem_recoverable("zoom", zoom_camp, level, true); + write_mem_recoverable("zoom", zoom_telescope, level, true); // overwrite the current value auto game_api = GameAPI::get(); game_api->set_zoom(std::nullopt, level); } +void State::zoom_reset() +{ + recover_mem("zoom"); + auto game_api = GameAPI::get(); + game_api->set_zoom(std::nullopt, 13.5f); +} + void StateMemory::force_current_theme(uint32_t t) { if (t > 0 && t < 19) @@ -446,10 +453,16 @@ std::pair State::get_camera_position() void State::set_camera_position(float cx, float cy) { + static const auto addr = (float*)get_address("camera_position"); auto camera = ptr()->camera; - camera->focused_entity_uid = -1; camera->focus_x = cx; camera->focus_y = cy; + camera->adjusted_focus_x = cx; + camera->adjusted_focus_y = cy; + camera->calculated_focus_x = cx; + camera->calculated_focus_y = cy; + *addr = cx; + *(addr + 1) = cy; } void State::warp(uint8_t w, uint8_t l, uint8_t t) @@ -617,6 +630,10 @@ uint32_t State::get_frame_count() const { return memory_read((size_t)ptr() - 0xd0); } +int64_t get_global_frame_count() +{ + return global_frame_count; +}; std::vector State::read_prng() const { @@ -632,6 +649,15 @@ using OnStateUpdate = void(StateMemory*); OnStateUpdate* g_state_update_trampoline{nullptr}; void StateUpdate(StateMemory* s) { + auto state = State::get(); + if (s == state.ptr_main()) + { + if (global_frame_count < state.get_frame_count()) + global_frame_count = state.get_frame_count_main(); + else + global_frame_count++; + } + if (!pre_state_update()) { g_state_update_trampoline(s); diff --git a/src/game_api/state.hpp b/src/game_api/state.hpp index 2f694a1de..f06a11c01 100644 --- a/src/game_api/state.hpp +++ b/src/game_api/state.hpp @@ -338,6 +338,7 @@ struct State void darkmode(bool g); void zoom(float level); + void zoom_reset(); static std::pair click_position(float x, float y); static std::pair screen_position(float x, float y); @@ -379,3 +380,5 @@ uint8_t enum_to_layer(const LAYER layer); uint32_t lowbias32(uint32_t x); uint32_t lowbias32_r(uint32_t x); + +int64_t get_global_frame_count(); diff --git a/src/injected/ui.cpp b/src/injected/ui.cpp index 9f6151730..7191178fc 100644 --- a/src/injected/ui.cpp +++ b/src/injected/ui.cpp @@ -29,6 +29,7 @@ #include "olfont.h" +#include "bucket.hpp" #include "console.hpp" #include "custom_types.hpp" #include "entities_chars.hpp" @@ -38,6 +39,7 @@ #include "entities_mounts.hpp" #include "file_api.hpp" #include "flags.hpp" +#include "game_api.hpp" #include "game_manager.hpp" #include "illumination.hpp" #include "items.hpp" @@ -96,7 +98,8 @@ std::map default_keys{ {"toggle_disable_pause", OL_KEY_CTRL | OL_KEY_SHIFT | 'P'}, {"toggle_grid", OL_KEY_CTRL | OL_KEY_SHIFT | 'G'}, {"toggle_hitboxes", OL_KEY_CTRL | OL_KEY_SHIFT | 'H'}, - {"toggle_entityinfo", OL_KEY_CTRL | OL_KEY_SHIFT | 'E'}, + {"toggle_entity_info", OL_KEY_CTRL | OL_KEY_SHIFT | 'E'}, + {"toggle_entity_tooltip", OL_KEY_CTRL | OL_KEY_SHIFT | 'Y'}, {"toggle_hud", OL_KEY_CTRL | 'H'}, {"toggle_lights", OL_KEY_CTRL | 'L'}, {"toggle_ghost", OL_KEY_CTRL | 'O'}, @@ -155,7 +158,8 @@ std::map default_keys{ {"switch_ui", VK_F12}, {"zoom_in", OL_KEY_CTRL | VK_OEM_COMMA}, {"zoom_out", OL_KEY_CTRL | VK_OEM_PERIOD}, - {"zoom_default", OL_KEY_CTRL | '2'}, + {"zoom_reset", OL_KEY_CTRL | '1'}, + {"zoom_2x", OL_KEY_CTRL | '2'}, {"zoom_3x", OL_KEY_CTRL | '3'}, {"zoom_4x", OL_KEY_CTRL | '4'}, {"zoom_5x", OL_KEY_CTRL | '5'}, @@ -249,7 +253,7 @@ static ImFont *font, *bigfont, *hugefont; float g_x = 0, g_y = 0, g_vx = 0, g_vy = 0, g_dx = 0, g_dy = 0, g_zoom = 13.5f, g_hue = 0.63f, g_sat = 0.66f, g_val = 0.66f, g_camera_speed = 1.0f; ImVec2 startpos; int g_held_id = -1, g_last_id = -1, g_over_id = -1, g_current_item = 0, g_filtered_count = 0, g_last_frame = 0, - g_last_gun = 0, g_last_time = -1, g_level_time = -1, g_total_time = -1, g_pause_time = -1, + g_last_gun = 0, g_last_time = -1, g_level_time = -1, g_total_time = -1, g_force_width = 0, g_force_height = 0, g_pause_at = -1, g_hitbox_mask = 0x80BF, g_last_type = -1, g_force_level_width = 4, g_force_level_height = 4; unsigned int g_level_width = 0, grid_x = 0, grid_y = 0, g_pause_type = 0x2; uint8_t g_level = 1, g_world = 1, g_to = 0; @@ -269,13 +273,14 @@ std::vector g_selected_ids; bool set_focus_entity = false, set_focus_world = false, set_focus_zoom = false, set_focus_finder = false, set_focus_uid = false, scroll_to_entity = false, scroll_top = false, click_teleport = false, throw_held = false, paused = false, show_app_metrics = false, lock_entity = false, lock_player = false, freeze_last = false, freeze_level = false, freeze_total = false, hide_ui = false, - enable_noclip = false, load_script_dir = true, load_packs_dir = false, enable_camp_camera = true, enable_camera_bounds = true, freeze_quest_yang = false, freeze_quest_sisters = false, freeze_quest_horsing = false, freeze_quest_sparrow = false, freeze_quest_tusk = false, freeze_quest_beg = false, run_finder = false, in_menu = false, zooming = false, g_inv = false, edit_last_id = false, edit_achievements = false, peek_layer = false, pause_updates = true, death_disable = false; + enable_noclip = false, load_script_dir = true, load_packs_dir = false, enable_camp_camera = true, enable_camera_bounds = true, freeze_quest_yang = false, freeze_quest_sisters = false, freeze_quest_horsing = false, freeze_quest_sparrow = false, freeze_quest_tusk = false, freeze_quest_beg = false, run_finder = false, in_menu = false, zooming = false, g_inv = false, edit_last_id = false, edit_achievements = false, peek_layer = false, death_disable = false, camera_hack = false; std::optional quest_yang_state, quest_sisters_state, quest_horsing_state, quest_sparrow_state, quest_tusk_state, quest_beg_state; Entity* g_entity = 0; Entity* g_held_entity = 0; StateMemory* g_state = 0; SaveData* g_save = 0; GameManager* g_game_manager = 0; +Bucket* g_bucket = 0; std::map entity_names; std::map entity_full_names; std::string active_tab = "", activate_tab = "", detach_tab = "", focused_tool = "", g_load_void = ""; @@ -331,7 +336,8 @@ std::map options = { {"disable_pause", false}, {"draw_grid", false}, {"draw_hitboxes", false}, - {"draw_entityinfo", false}, + {"draw_entity_info", false}, + {"draw_entity_tooltip", false}, {"enable_unsafe_scripts", false}, {"warp_increments_level_count", true}, {"warp_transition", false}, @@ -1288,11 +1294,11 @@ std::optional get_spawn_item() auto to_spawn = g_items[g_filtered_items[g_current_item]]; if (g_current_item == 0 && (unsigned)g_filtered_count == g_items.size()) { - if (g_entity) + if (g_entity && g_entity->type->id <= to_id("ENT_TYPE_LIQUID_COARSE_LAVA")) { to_spawn = EntityItem{entity_full_names[g_entity->type->id], g_entity->type->id}; } - else if (g_last_type >= 0) + else if (g_last_type >= 0 && g_last_type <= (int)to_id("ENT_TYPE_LIQUID_COARSE_LAVA")) { to_spawn = EntityItem{entity_full_names[g_last_type], (uint32_t)g_last_type}; } @@ -1561,12 +1567,24 @@ void spawn_entity_over() { int spawned = UI::spawn_entity_over(item.id, g_over_id, g_dx, g_dy); auto ent = get_entity_ptr(spawned); - ent->set_draw_depth(9); - ent->flags = set_flag(ent->flags, 4); // pass thru objects - ent->flags = set_flag(ent->flags, 10); // no gravity - ent->flags = clr_flag(ent->flags, 13); // collides walls - if (!test_flag(g_state->special_visibility_flags, 1)) - ent->flags = set_flag(ent->flags, 1); // invisible + if (g_dx == 0 && g_dy == 0) + { + ent->set_draw_depth(9); + ent->flags = set_flag(ent->flags, 4); // pass thru objects + ent->flags = set_flag(ent->flags, 10); // no gravity + ent->flags = clr_flag(ent->flags, 13); // collides walls + if (!test_flag(g_state->special_visibility_flags, 1)) + ent->flags = set_flag(ent->flags, 1); // invisible + } + if (item.id == to_id("ENT_TYPE_ITEM_EGGSAC")) + { + if (g_dx == -1 and g_dy == 0) + ent->angle = 3.14159265358979323846f / 2.0f; + else if (g_dx == 1 and g_dy == 0) + ent->angle = 3.0f * 3.14159265358979323846f / 2.0f; + else if (g_dx == 0 and g_dy == -1) + ent->angle = 3.14159265358979323846f; + } if (!lock_entity) g_last_id = spawned; } @@ -2103,52 +2121,21 @@ void force_kits() bool toggle_pause() { g_pause_at = -1; - g_pause_time = -1; - if (g_pause_type & 0x40) - { - if (!paused) - { - g_pause_time = g_state->time_startup; - pause_updates = true; - paused = true; - } - else - { - g_pause_time = -1; - pause_updates = false; - paused = false; - } - g_ui_scripts["pause"]->execute(fmt::format("pause_at = {}", g_pause_time)); - } - else - { - if (g_state->pause == 0) - { - g_state->pause = (uint8_t)g_pause_type; - paused = true; - } - else - { - g_state->pause = 0; - paused = false; - } - } + g_state->pause ^= ((uint8_t)g_pause_type & ~0x40); + g_ui_scripts["pause"]->execute(fmt::format("exports.type = {} exports.paused = {} exports.skip = false", g_pause_type, paused)); return paused; } void frame_advance() { - if (g_pause_type & 0x40) + if (g_state->pause == 0 && g_pause_at != -1 && (unsigned)g_pause_at <= UI::get_frame_count()) { - g_ui_scripts["pause"]->execute(fmt::format("pause_at = {}", g_pause_time)); + g_state->pause = (uint8_t)g_pause_type & ~0x40; + g_pause_at = -1; } - else + if ((g_pause_type & 0x40) == 0) { - if (g_state->pause == 0 && g_pause_at != -1 && (unsigned)g_pause_at <= UI::get_frame_count()) - { - g_state->pause = (uint8_t)g_pause_type; - g_pause_at = -1; - } + paused = g_state->pause & (uint8_t)g_pause_type; } } @@ -2332,6 +2319,9 @@ void respawn() bool pressed(std::string keyname, WPARAM wParam) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { return false; @@ -2349,11 +2339,14 @@ bool pressed(std::string keyname, WPARAM wParam) { wParam += OL_KEY_ALT; } - return wParam == (unsigned)keycode; + return wParam == (unsigned)keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool pressing(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { return false; @@ -2374,11 +2367,14 @@ bool pressing(std::string keyname) { wParam += OL_KEY_ALT; } - return wParam == (unsigned)keycode && (GetKeyState(key) & 0x8000); + return wParam == (unsigned)keycode && (GetKeyState(key) & 0x8000) && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool clicked(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2419,11 +2415,14 @@ bool clicked(std::string keyname) } } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool dblclicked(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2450,11 +2449,14 @@ bool dblclicked(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool held(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2481,11 +2483,14 @@ bool held(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool released(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2512,11 +2517,14 @@ bool released(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool dragging(std::string keyname) { + if (g_bucket->overlunky->ignore_keys.contains(keyname)) + return false; + int wParam = OL_BUTTON_MOUSE; if (keys.find(keyname) == keys.end() || (keys[keyname] & 0xff) == 0) { @@ -2543,7 +2551,7 @@ bool dragging(std::string keyname) break; } } - return wParam == keycode; + return wParam == keycode && !g_bucket->overlunky->ignore_keycodes.contains(keycode); } bool dragged(std::string keyname) // unused @@ -2659,6 +2667,20 @@ void set_selected_type(uint32_t id) g_last_type = id; } +void toggle_lights() +{ + if (options["lights"] && g_state->illumination) + { + g_state->illumination->flags |= (1U << 24); + } + else if (!options["lights"] && g_state->illumination) + { + g_state->illumination->flags &= ~(1U << 16); + if ((g_state->level_flags & (1U << 17)) > 0) + g_state->illumination->flags &= ~(1U << 24); + } +} + bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) { ImGuiContext& g = *GImGui; @@ -2860,9 +2882,14 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) g_zoom -= 1.0f; set_zoom(); } - else if (pressed("zoom_default", wParam)) + else if (pressed("zoom_reset", wParam)) { g_zoom = 13.5f; + UI::zoom_reset(); + } + else if (pressed("zoom_2x", wParam)) + { + g_zoom = 16.324f; set_zoom(); } else if (pressed("zoom_3x", wParam)) @@ -2919,9 +2946,13 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) { options["draw_hitboxes"] = !options["draw_hitboxes"]; } - else if (pressed("toggle_entityinfo", wParam)) + else if (pressed("toggle_entity_info", wParam)) { - options["draw_entityinfo"] = !options["draw_entityinfo"]; + options["draw_entity_info"] = !options["draw_entity_info"]; + } + else if (pressed("toggle_entity_tooltip", wParam)) + { + options["draw_entity_tooltip"] = !options["draw_entity_tooltip"]; } else if (pressed("toggle_grid", wParam)) { @@ -2941,6 +2972,7 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) } else if (pressed("toggle_pause", wParam)) { + paused = !paused; toggle_pause(); } else if (pressed("toggle_hud", wParam)) @@ -2955,10 +2987,9 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) { if (g_pause_type & 0x40) { - if (pause_updates) + if (paused) { - g_pause_time = g_state->time_startup + 1; - g_ui_scripts["pause"]->execute(fmt::format("pause_at = {}", g_pause_time)); + g_ui_scripts["pause"]->execute("exports.skip = true"); } } else @@ -2978,16 +3009,7 @@ bool process_keys(UINT nCode, WPARAM wParam, [[maybe_unused]] LPARAM lParam) else if (pressed("toggle_lights", wParam)) { options["lights"] = !options["lights"]; - if (options["lights"] && g_state->illumination) - { - g_state->illumination->flags |= (1U << 24); - } - else if (!options["lights"] && g_state->illumination) - { - g_state->illumination->flags &= ~(1U << 16); - if ((g_state->level_flags & (1U << 17)) > 0) - g_state->illumination->flags &= ~(1U << 24); - } + toggle_lights(); } else if (pressed("toggle_ghost", wParam)) { @@ -3945,6 +3967,14 @@ void render_narnia() tooltip("Simulate natural level progression even more."); } +void set_camera_hack(bool enable) +{ + camera_hack = enable; + if (g_ui_scripts.find("camera_hack") == g_ui_scripts.end()) + return; + g_ui_scripts["camera_hack"]->set_enabled(camera_hack); +} + void render_camera() { if (set_focus_zoom) @@ -3962,9 +3992,16 @@ void render_camera() if (ImGui::Button("Default")) { g_zoom = 13.5f; + UI::zoom_reset(); + } + tooltip("Default zoom level.", "zoom_reset"); + ImGui::SameLine(); + if (ImGui::Button("2x")) + { + g_zoom = 16.324f; set_zoom(); } - tooltip("Default zoom level.", "zoom_default"); + tooltip("2 room wide zoom level.", "zoom_2x"); ImGui::SameLine(); if (ImGui::Button("3x")) { @@ -4023,31 +4060,35 @@ void render_camera() ImGui::InputFloat("Camera Focus Y##CameraFocusY", &g_state->camera->focus_y, 0.2f, 1.0f); ImGui::InputFloat("Camera Real X##CameraRealX", &g_state->camera->adjusted_focus_x, 0.2f, 1.0f); ImGui::InputFloat("Camera Real Y##CameraRealY", &g_state->camera->adjusted_focus_y, 0.2f, 1.0f); - if (submenu("Camera Bounds")) + ImGui::SeparatorText("Camera bounds"); + ImGui::InputFloat("Top##CameraBoundTop", &g_state->camera->bounds_top, 0.2f, 1.0f); + ImGui::InputFloat("Bottom##CameraBoundBottom", &g_state->camera->bounds_bottom, 0.2f, 1.0f); + ImGui::InputFloat("Left##CameraBoundLeft", &g_state->camera->bounds_left, 0.2f, 1.0f); + ImGui::InputFloat("Right##CameraBoundRight", &g_state->camera->bounds_right, 0.2f, 1.0f); + if (ImGui::Checkbox("Enable default camera bounds##CameraBoundsLevel", &enable_camera_bounds)) + set_camera_bounds(enable_camera_bounds); + tooltip("Disable to free the camera bounds in a level.\nAutomatically disabled when dragging.", "camera_reset"); + if (ImGui::Checkbox("Enable camp camera bounds##CameraBoundsCamp", &enable_camp_camera)) { - ImGui::InputFloat("Top##CameraBoundTop", &g_state->camera->bounds_top, 0.2f, 1.0f); - ImGui::InputFloat("Bottom##CameraBoundBottom", &g_state->camera->bounds_bottom, 0.2f, 1.0f); - ImGui::InputFloat("Left##CameraBoundLeft", &g_state->camera->bounds_left, 0.2f, 1.0f); - ImGui::InputFloat("Right##CameraBoundRight", &g_state->camera->bounds_right, 0.2f, 1.0f); - if (ImGui::Checkbox("Enable default camera bounds##CameraBoundsLevel", &enable_camera_bounds)) - { - set_camera_bounds(enable_camera_bounds); - } - tooltip("Disable to free the camera bounds in a level.\nAutomatically disabled when dragging.", "camera_reset"); - if (ImGui::Checkbox("Enable camp camera bounds##CameraBoundsCamp", &enable_camp_camera)) + UI::set_camp_camera_bounds_enabled(enable_camp_camera); + if (!enable_camp_camera && g_state->screen == 11) { - UI::set_camp_camera_bounds_enabled(enable_camp_camera); - if (!enable_camp_camera && g_state->screen == 11) - { - g_state->camera->bounds_left = 0.5; - g_state->camera->bounds_right = 74.5; - g_state->camera->bounds_top = 124.5; - g_state->camera->bounds_bottom = 56.5; - } + g_state->camera->bounds_left = 0.5; + g_state->camera->bounds_right = 74.5; + g_state->camera->bounds_top = 124.5; + g_state->camera->bounds_bottom = 56.5; } - tooltip("Disable to free the camera in camp.\nAutomatically disabled when zooming."); - endmenu(); } + tooltip("Disable to free the camera in camp.\nAutomatically disabled when zooming."); + if (ImGui::Checkbox("Follow focused entity absolutely##CameraForcePlayer", &camera_hack)) + { + set_camera_hack(camera_hack); + enable_camera_bounds = !camera_hack; + set_camera_bounds(enable_camera_bounds); + enable_camp_camera = !camera_hack; + UI::set_camp_camera_bounds_enabled(enable_camp_camera); + } + tooltip("Enable to remove camera bounds and always center the entity instantly without rubberbanding."); } void render_arrow() @@ -4399,6 +4440,11 @@ std::string entity_tooltip(Entity* hovered) if (chest->leprechaun) coords += " (LEPRECHAUN)"; } + else if (hovered->type->id == to_id("ENT_TYPE_ITEM_BOMB")) + { + auto bomb = hovered->as(); + coords += fmt::format(" ({} FUSE)", 150 - bomb->idle_counter); + } else if (hovered->type->search_flags & 7) { auto ent = hovered->as(); @@ -4465,7 +4511,7 @@ void render_hitbox(Entity* ent, bool cross, ImColor color, bool filled = false, else draw_list->AddRect(fix_pos(sboxa), fix_pos(sboxb), color, rounded ? 5.0f : 0.0f, 0, 2.0f); - if (options["draw_entityinfo"] && !cross && !filled) + if (options["draw_entity_info"] && !cross && !filled) draw_list->AddText(fix_pos(ImVec2(sboxa.x, sboxb.y)), ImColor(1.0f, 1.0f, 1.0f, 0.8f), entity_tooltip(ent).c_str()); if ((g_hitbox_mask & 0x8000) == 0) @@ -4479,6 +4525,8 @@ void render_hitbox(Entity* ent, bool cross, ImColor color, bool filled = false, static const auto mine = to_id("ENT_TYPE_ITEM_LANDMINE"); static const auto keg = to_id("ENT_TYPE_ACTIVEFLOOR_POWDERKEG"); static const auto keg2 = to_id("ENT_TYPE_ACTIVEFLOOR_TIMEDPOWDERKEG"); + static const auto sun_generator = to_id("ENT_TYPE_FLOOR_SUNCHALLENGE_GENERATOR"); + static const auto shoppie_generator = to_id("ENT_TYPE_FLOOR_SHOPKEEPER_GENERATOR"); if (type == spark_trap && ent->animation_frame == 7) { @@ -4489,6 +4537,28 @@ void render_hitbox(Entity* ent, bool cross, ImColor color, bool filled = false, auto srad = screenify({radx, rady}); draw_list->AddCircle(fix_pos(spos), srad.x - spos.x, ImColor(255, 0, 0, 40), 0, sthick); } + else if ((type == sun_generator || type == shoppie_generator) && !g_players.empty()) + { + auto gen = ent->as(); + float rad = 8.0f; + float min_dist = 100.0f; + for (auto p : g_players) + { + auto [x, y] = UI::get_position(p); + auto a = Vec2(x, y); + auto b = Vec2(gen->x, gen->y); + auto dist = a.distance_to(b); + if (dist < min_dist) + min_dist = dist; + } + bool enabled = min_dist < 8.0f && (type != sun_generator || gen->on_off); + if (cross || enabled || min_dist < 8.0f) + { + auto [radx, rady] = UI::screen_position(render_position.first + rad, render_position.second + rad); + auto srad = screenify({radx, rady}); + draw_list->AddCircle(fix_pos(spos), srad.x - spos.x, enabled ? ImColor(255, 0, 0, 80) : ImColor(0, 200, 128, 60), 0, 2.0f); + } + } else if (type == bomb) { float rad = 1.6f; @@ -4792,6 +4862,9 @@ void render_clickhandler() to_id("ENT_TYPE_FLOOR_TENTACLE_BOTTOM"), to_id("ENT_TYPE_FLOOR_TELEPORTINGBORDER"), to_id("ENT_TYPE_FLOOR_SPIKES"), + to_id("ENT_TYPE_FLOOR_FACTORY_GENERATOR"), + to_id("ENT_TYPE_FLOOR_SHOPKEEPER_GENERATOR"), + to_id("ENT_TYPE_FLOOR_SUNCHALLENGE_GENERATOR"), }; for (auto entity : UI::get_entities_by(additional_fixed_entities, 0x180, (LAYER)(peek_layer ? g_state->camera_layer ^ 1 : g_state->camera_layer))) // FLOOR | ACTIVEFLOOR { @@ -4821,23 +4894,32 @@ void render_clickhandler() grids = screenify({gridline.first, gridline.second}); draw_list->AddLine(fix_pos(ImVec2(base->Pos.x, grids.y)), fix_pos(ImVec2(base->Pos.x + base->Size.x, grids.y)), ImColor(255, 0, 0, 150), 2.0f); } + } - if (ImGui::IsMousePosValid()) + if (ImGui::IsMousePosValid()) + { + ImVec2 mpos = normalize(mouse_pos()); + std::pair cpos = UI::click_position(mpos.x, mpos.y); + std::string coords = fmt::format("{:.2f}, {:.2f} ({:.2f}, {:.2f})", cpos.first, cpos.second, mpos.x, mpos.y); + unsigned int mask = safe_entity_mask; + if (ImGui::GetIO().KeyShift) // TODO: Get the right modifier from mouse_destroy_unsafe { - ImVec2 mpos = normalize(mouse_pos()); - std::pair cpos = UI::click_position(mpos.x, mpos.y); - std::string coords = fmt::format("{:.2f}, {:.2f} ({:.2f}, {:.2f})", cpos.first, cpos.second, mpos.x, mpos.y); - unsigned int mask = safe_entity_mask; - if (ImGui::GetIO().KeyShift) // TODO: Get the right modifier from mouse_destroy_unsafe - { - mask = unsafe_entity_mask; - } - auto hovered = UI::get_entity_at(cpos.first, cpos.second, false, 2, mask); - if (hovered != nullptr) - { + mask = unsafe_entity_mask; + } + auto hovered = UI::get_entity_at(cpos.first, cpos.second, false, 2, mask); + if (hovered) + { + if (options["draw_hitboxes"]) render_hitbox(hovered, true, ImColor(50, 50, 255, 200)); - coords += "\n" + entity_tooltip(hovered); - } + coords += "\n" + entity_tooltip(hovered); + g_bucket->overlunky->hovered_uid = hovered->uid; + } + else + { + g_bucket->overlunky->hovered_uid = -1; + } + if (options["draw_entity_tooltip"]) + { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {4.0f, 4.0f}); tooltip(coords.c_str(), true); ImGui::PopStyleVar(); @@ -5287,11 +5369,8 @@ void render_clickhandler() g_state->camera->focus_x -= (current_pos.first - oryginal_pos.first) * g_camera_speed; g_state->camera->focus_y -= (current_pos.second - oryginal_pos.second) * g_camera_speed; - if (g_state->pause != 0 || !options["smooth_camera"]) - { - g_state->camera->adjusted_focus_x = g_state->camera->focus_x; - g_state->camera->adjusted_focus_y = g_state->camera->focus_y; - } + if (g_state->pause != 0 || paused || !options["smooth_camera"]) + State::get().set_camera_position(g_state->camera->focus_x, g_state->camera->focus_y); startpos = normalize(mouse_pos()); enable_camera_bounds = false; set_camera_bounds(enable_camera_bounds); @@ -5434,6 +5513,30 @@ void render_keyconfig() keys = default_keys; save_config(cfgfile); } + if (g_bucket->overlunky->ignore_keys.size()) + { + std::string s; + for (auto const& e : g_bucket->overlunky->ignore_keys) + { + s += e; + s += ','; + } + s.pop_back(); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "The following keys are disabled by scripts:"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", s.c_str()); + } + if (g_bucket->overlunky->ignore_keycodes.size()) + { + std::string s; + for (auto const& e : g_bucket->overlunky->ignore_keycodes) + { + s += fmt::format("0x{:x}", e); + s += ','; + } + s.pop_back(); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "The following keycodes are disabled by scripts:"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", s.c_str()); + } ImGui::BeginTable("##keyconfig", 4); ImGui::TableSetupColumn("Tool"); ImGui::TableSetupColumn("Keys"); @@ -5720,8 +5823,10 @@ void render_options() ImGui::SameLine(); ImGui::Checkbox("interpolated##DrawRealBox", &options["draw_hitboxes_interpolated"]); tooltip("Use interpolated render position for smoother hitboxes on hifps.\nActual game logic is not interpolated like this though."); - ImGui::Checkbox("Draw entity info##DrawEntityInfo", &options["draw_entityinfo"]); - tooltip("Draw entity names, uids and some random stuff next to the hitboxes.", "toggle_entityinfo"); + ImGui::Checkbox("Draw hovered entity tooltip##DrawEntityTooltip", &options["draw_entity_tooltip"]); + tooltip("Draw entity names, uids and some random stuff for hovered entitites.", "toggle_entity_tooltip"); + ImGui::Checkbox("Draw all entity info##DrawEntityInfo", &options["draw_entity_info"]); + tooltip("Draw entity names, uids and some random stuff next to all entities.", "toggle_entity_info"); ImGui::Checkbox("Smooth camera dragging", &options["smooth_camera"]); tooltip("Smooth camera movement when dragging, unless paused."); ImGui::SliderFloat("Camera speed##DragSpeed", &g_camera_speed, 1.0f, 5.0f); @@ -5907,12 +6012,113 @@ std::string gen_random(const int len) return tmp_s; } +void set_default_path(IFileDialog* dialog, std::wstring pathStr) +{ + const wchar_t* defaultPathW = pathStr.c_str(); + + IShellItem* folder; + HRESULT result = SHCreateItemFromParsingName(defaultPathW, NULL, IID_PPV_ARGS(&folder)); + + // Valid non results. + if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE)) + return; + + if (!SUCCEEDED(result)) + return; + + dialog->SetFolder(folder); + folder->Release(); +} + +std::vector select_files(std::wstring default_path, DWORD type = 0) +{ + HRESULT hr = S_OK; + std::vector filePaths; + + IFileOpenDialog* fileDlg = NULL; + hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fileDlg)); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT(fileDlg->Release()); + set_default_path(fileDlg, default_path); + + IKnownFolderManager* pkfm = NULL; + hr = CoCreateInstance(CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pkfm)); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT(pkfm->Release()); + + IKnownFolder* pKnownFolder = NULL; + hr = pkfm->GetFolder(FOLDERID_PublicMusic, &pKnownFolder); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT(pKnownFolder->Release()); + + IShellItem* psi = NULL; + hr = pKnownFolder->GetShellItem(0, IID_PPV_ARGS(&psi)); + if (FAILED(hr)) + return filePaths; + ON_SCOPE_EXIT(psi->Release()); + + hr = fileDlg->AddPlace(psi, FDAP_BOTTOM); + + DWORD dwOptions; + fileDlg->GetOptions(&dwOptions); + fileDlg->SetOptions(dwOptions | type); + hr = fileDlg->Show(NULL); + if (SUCCEEDED(hr)) + { + IShellItemArray* pRets; + hr = fileDlg->GetResults(&pRets); + if (SUCCEEDED(hr)) + { + DWORD count; + pRets->GetCount(&count); + for (DWORD i = 0; i < count; i++) + { + IShellItem* pRet; + LPWSTR nameBuffer; + pRets->GetItemAt(i, &pRet); + pRet->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &nameBuffer); + filePaths.push_back(std::wstring(nameBuffer)); + pRet->Release(); + CoTaskMemFree(nameBuffer); + } + pRets->Release(); + } + } + return filePaths; +} + +void set_script_dir() +{ + TCHAR buffer[MAX_PATH] = {0}; + GetModuleFileName(NULL, buffer, MAX_PATH); + auto path = std::filesystem::path(std::string(buffer)).parent_path(); + DEBUG("def {}", path.string()); + ShowCursor(true); + auto folder = select_files(path.wstring(), FOS_PICKFOLDERS); + if (!folder.empty()) + { + scriptpath = std::string(cvt.to_bytes(folder.at(0))); + std::replace(scriptpath.begin(), scriptpath.end(), '\\', '/'); + save_config(cfgfile); + refresh_script_files(); + } + ShowCursor(false); +} + void render_script_files() { ImGui::PushID("files"); int num = 0; for (auto file : g_script_files) { + auto id = file.string(); + std::replace(id.begin(), id.end(), '\\', '/'); + if (g_scripts.count(id) > 0) + continue; + ImGui::PushID(num++); auto buttpath = file.parent_path().filename() / file.filename(); std::wstring wbuttstr = buttpath.wstring(); @@ -5924,23 +6130,42 @@ void render_script_files() } ImGui::PopID(); } + std::filesystem::path path = scriptpath; + std::string abspath = scriptpath; + if (std::filesystem::exists(abspath) && std::filesystem::is_directory(abspath)) + { + abspath = std::filesystem::absolute(path).string(); + } if (g_script_files.size() == 0) { - std::filesystem::path path = scriptpath; - std::string abspath = scriptpath; - if (std::filesystem::exists(abspath) && std::filesystem::is_directory(abspath)) - { - abspath = std::filesystem::absolute(path).string(); - } ImGui::TextWrapped("No scripts found. Put .lua files in '%s' or change script_dir in the ini file and reload.", abspath.c_str()); } - if (ImGui::Button("Refresh##RefreshScripts")) + else if (g_script_files.size() > 0 && num == 0) + { + ImGui::TextWrapped("All scripts found in '%s' are already loaded.", abspath.c_str()); + } + if (ImGui::Button("Select script directory##SelectScriptDir")) + set_script_dir(); + tooltip(scriptpath.c_str()); + if (ImGui::Button("Reload config and refresh scripts##RefreshScripts")) { + load_config(cfgfile); refresh_script_files(); } ImGui::PopID(); } +bool lower_test(char l, char r) +{ + return (std::tolower(l) == std::tolower(r)); +} + +bool find_match(std::string needle, std::string haystack) +{ + std::string::iterator fpos = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), lower_test); + return fpos != haystack.end(); +} + void render_scripts() { ImGui::PushTextWrapPos(0.0f); @@ -5949,8 +6174,12 @@ void render_scripts() ImGui::SameLine(); ImGui::Checkbox("to console##ConsoleScriptMessages", &options["console_script_messages"]); ImGui::Checkbox("Fade script messages##FadeScriptMessages", &options["fade_script_messages"]); - if (ImGui::Checkbox("Load scripts from default directory##LoadScriptsDefault", &load_script_dir)) + if (ImGui::Checkbox("Load scripts from script directory##LoadScriptsDefault", &load_script_dir)) refresh_script_files(); + ImGui::SameLine(); + if (ImGui::Button("Set##SetScriptDir")) + set_script_dir(); + tooltip(scriptpath.c_str()); if (ImGui::Checkbox("Load scripts from Mods/Packs##LoadScriptsPacks", &load_packs_dir)) refresh_script_files(); if (ImGui::Button("Create new quick script")) @@ -5969,6 +6198,11 @@ void render_scripts() ImGui::SameLine(); static bool enabled_only{false}; ImGui::Checkbox("Hide disabled##EnabledScriptsOnly", &enabled_only); + static std::string script_filter; + ImGui::InputText("Search##ScriptFilter", &script_filter); + ImGui::SameLine(); + if (ImGui::Button("Clear##ClearScriptFilter")) + script_filter.clear(); ImGui::PushItemWidth(-1); int i = 0; std::vector unload_scripts; @@ -5981,6 +6215,8 @@ void render_scripts() { if (enabled_only && !script->is_enabled()) continue; + if (!script_filter.empty() && !find_match(script_filter, script->get_name() + " " + script->get_path() + " " + script->get_id())) + continue; ImGui::PushID(i); ImGui::PushID(script_name.c_str()); std::string filename; @@ -8125,6 +8361,13 @@ void render_game_props() } endmenu(); } + if (submenu("Renderer flags")) + { + auto game_api = GameAPI::get(); + ImGui::SeparatorText("Flags 1"); + render_flags(renderer_flags1, &game_api->renderer->flags1); + endmenu(); + } if (submenu("Procedural chances")) { static auto hide_zero = true; @@ -8694,6 +8937,20 @@ void imgui_pre_init(ImGuiContext*) io.ConfigViewportsNoTaskBarIcon = true; } +void add_ui_script(std::string name, bool enable, std::string code) +{ + if (g_ui_scripts.find(name) == g_ui_scripts.end()) + { + SpelunkyScript* script = new SpelunkyScript( + code, + name, + g_SoundManager.get(), + g_Console.get(), + enable); + g_ui_scripts[name] = std::unique_ptr(script); + } +} + void imgui_init(ImGuiContext*) { if (std::setlocale(LC_CTYPE, ".UTF-8") == nullptr) @@ -8723,42 +8980,44 @@ void imgui_init(ImGuiContext*) windows["tool_texture"] = new Window({"Texture viewer", is_tab_detached("tool_texture"), is_tab_open("tool_texture")}); // windows["tool_sound"] = new Window({"Sound player", is_tab_detached("tool_sound"), is_tab_open("tool_sound")}); - if (g_ui_scripts.find("pause") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - R"( -set_callback(function() return pause_at and pause_at ~= -1 and state.time_startup >= pause_at end, ON.PRE_UPDATE) - )", - "pause", - g_SoundManager.get(), - g_Console.get(), - true); - g_ui_scripts["pause"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("dark") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - "set_callback(function() state.level_flags = set_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)", - "dark", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["dark"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("light") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - "set_callback(function() state.level_flags = clr_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)", - "light", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["light"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("void") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - R"( + add_ui_script("pause", true, R"( +meta = { + name = "pause", + author = "overlunky", +} +exports = { + type = 0, + paused = false, + skip = false, +} +set_callback(function() + if test_mask(exports.type, 0x40) then + if exports.paused and exports.skip then + exports.skip = false + state.pause = clr_mask(state.pause, exports.type) + return false + elseif exports.paused then + state.pause = set_mask(state.pause, clr_mask(exports.type, 0x40)) + end + return exports.paused + end +end, ON.PRE_UPDATE))"); + add_ui_script("camera_hack", false, R"( +lastpos = Vec2:new() +set_callback(function() + local e = get_entity(state.camera.focused_entity_uid) + if not e then return end + local x,y,l = get_render_position(e.uid) + local pos = Vec2:new(x, y) + if pos:distance_to(lastpos) > 1 then x,y,l = get_position(e.uid) end + lastpos = pos + x = x + state.camera.focus_offset_x + y = y + state.camera.vertical_pan + state.camera.focus_offset_y + set_camera_position(x, y) +end, ON.RENDER_PRE_GAME))"); + add_ui_script("dark", false, "set_callback(function() state.level_flags = set_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)"); + add_ui_script("light", false, "set_callback(function() state.level_flags = clr_flag(state.level_flags, 18) end, ON.POST_ROOM_GENERATION)"); + add_ui_script("void", false, R"( qflags = {2,3,5,17,18,19,25,26,27} disable_virts = {2,3,4,5,6,7,8,9,10,11,12,15,16,17,18,19,20} hooks = {} @@ -8826,23 +9085,8 @@ end set_callback(init_hooks, ON.LOAD) set_callback(init_hooks, ON.SCRIPT_ENABLE) set_callback(clear_hooks, ON.SCRIPT_DISABLE) -)", - "void", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["void"] = std::unique_ptr(script); - } - if (g_ui_scripts.find("level_size") == g_ui_scripts.end()) - { - SpelunkyScript* script = new SpelunkyScript( - "", - "level_size", - g_SoundManager.get(), - g_Console.get(), - false); - g_ui_scripts["level_size"] = std::unique_ptr(script); - } +)"); + add_ui_script("level_size", false, ""); } void imgui_draw() @@ -9118,6 +9362,102 @@ void check_focus() } } +std::unordered_set legal_options{ + "disable_ghost_timer", + "disable_pause", + "draw_entity_info", + "draw_entity_tooltip", + "draw_grid", + "draw_hitboxes", + "draw_hitboxes_interpolated", + "draw_hotbar", + "draw_hud", + "draw_path", + "draw_script_messages", + "fade_script_messages", + "fly_mode", + "god_mode", + "god_mode_companions", + "hd_cursor", + "keyboard_control", + "lights", + "mouse_control", + "noclip", + "smooth_camera", + "pause_type", + "camera_hack", +}; + +void update_bucket() +{ + if (g_bucket->overlunky->set_selected_uid.has_value()) + { + g_last_id = g_bucket->overlunky->set_selected_uid.value(); + g_entity = get_entity_ptr(g_last_id); + g_bucket->overlunky->set_selected_uid = std::nullopt; + } + if (g_bucket->overlunky->set_selected_uids.has_value()) + { + g_selected_ids = g_bucket->overlunky->set_selected_uids.value(); + g_bucket->overlunky->set_selected_uids = std::nullopt; + } + g_bucket->overlunky->selected_uid = g_last_id; + g_bucket->overlunky->selected_uids = g_selected_ids; + g_bucket->overlunky->keys = keys; + for (auto [k, v] : options) + { + g_bucket->overlunky->options[k] = options[k]; + } + g_bucket->overlunky->options["pause_type"] = g_pause_type; + + for (auto [k, v] : g_bucket->overlunky->set_options) + { + if (!legal_options.contains(k)) + continue; + + if (options.contains(k)) + if (auto* val = std::get_if(&v)) + options[k] = *val; + + if (k == "disable_ghost_timer") + { + UI::set_time_ghost_enabled(!options["disable_ghost_timer"]); + UI::set_time_jelly_enabled(!options["disable_ghost_timer"]); + UI::set_cursepot_ghost_enabled(!options["disable_ghost_timer"]); + } + else if (k == "god_mode") + { + UI::godmode(options["god_mode"]); + } + else if (k == "god_mode") + { + UI::godmode_companions(options["god_mode_companions"]); + } + else if (k == "noclip") + { + toggle_noclip(); + } + else if (k == "lights") + { + toggle_lights(); + } + else if (k == "pause_type") + { + if (auto* val = std::get_if(&v)) + g_pause_type = (uint32_t)*val; + } + else if (k == "camera_hack") + { + if (auto* val = std::get_if(&v)) + { + camera_hack = (uint32_t)*val; + set_camera_hack(camera_hack); + } + } + } + g_bucket->overlunky->set_options.clear(); +} + void post_draw() { check_focus(); @@ -9129,6 +9469,7 @@ void post_draw() force_cheats(); force_lights(); frame_advance(); + update_bucket(); } void create_box(std::vector items) @@ -9174,6 +9515,8 @@ void init_ui() g_state = State::get().ptr_main(); g_save = UI::savedata(); g_game_manager = get_game_manager(); + g_bucket = Bucket::get(); + g_bucket->overlunky = new Overlunky(); g_Console = std::make_unique(g_SoundManager.get()); g_Console->set_max_history_size(1000); diff --git a/src/injected/ui.hpp b/src/injected/ui.hpp index 1ccddf8dd..7e92ff545 100644 --- a/src/injected/ui.hpp +++ b/src/injected/ui.hpp @@ -3,8 +3,44 @@ #include #include +#include // for true_type, is_invocable_r_v #include +template +requires(std::is_function_v) +struct is_invocable_as : std::false_type +{ +}; +template +struct is_invocable_as : std::is_invocable_r +{ +}; +template +inline constexpr auto is_invocable_as_v = is_invocable_as::value; + +template +requires is_invocable_as_v +struct OnScopeExit +{ + OnScopeExit(FunT&& fun) + : Fun{std::forward(fun)} + { + } + ~OnScopeExit() + { + Fun(); + } + FunT Fun; +}; + +#define OL_CONCAT_IMPL(x, y) x##y +#define OL_CONCAT(x, y) OL_CONCAT_IMPL(x, y) +#define ON_SCOPE_EXIT(expr) \ + OnScopeExit OL_CONCAT(on_scope_exit_, __LINE__) \ + { \ + [&]() { expr; } \ + } + const int OL_KEY_CTRL = 0x100; const int OL_KEY_SHIFT = 0x200; const int OL_KEY_ALT = 0x800; diff --git a/src/injected/ui_util.cpp b/src/injected/ui_util.cpp index 8a31c699c..bef25b70e 100644 --- a/src/injected/ui_util.cpp +++ b/src/injected/ui_util.cpp @@ -50,6 +50,10 @@ void UI::zoom(float level) { State::get().zoom(level); } +void UI::zoom_reset() +{ + State::get().zoom_reset(); +} uint32_t UI::get_frame_count() { return State::get().get_frame_count(); diff --git a/src/injected/ui_util.hpp b/src/injected/ui_util.hpp index 224cee8f2..36011dd2f 100644 --- a/src/injected/ui_util.hpp +++ b/src/injected/ui_util.hpp @@ -40,6 +40,7 @@ class UI static void death_enabled(bool g); static std::pair click_position(float x, float y); static void zoom(float level); + static void zoom_reset(); static uint32_t get_frame_count(); static void warp(uint8_t world, uint8_t level, uint8_t theme); static void transition(uint8_t world, uint8_t level, uint8_t theme);