--------------------------------------------------------------------------- -- Super Mario World 2 - Yoshi's Island (U) Utility Script for Snes9x - rr -- http://tasvideos.org/GameResources/SNES/YoshisIsland.html -- -- Author: BrunoValads -- Git repository: https://github.com/brunovalads/yoshis-island-stuff -- TODO -- -- Based on Amaraticando's Super Mario World script for Snes9x-rr -- http://github.com/rodamaral/smw-tas/blob/master/Snes9x/smw-snes9x.lua --------------------------------------------------------------------------- --############################################################################# -- CONFIG: local INI_CONFIG_FILENAME = "Yoshi's Island Utilities Config.ini" -- relative to the folder of the script local DEFAULT_OPTIONS = { -- Hotkeys -- make sure that the hotkeys below don't conflict with previous bindings hotkey_decrease_opacity = "numpad-", -- to decrease the opacity of the text hotkey_increase_opacity = "numpad+", -- to increase the opacity of the text -- Display display_movie_info = true, display_misc_info = true, display_player_info = true, display_player_hitbox = true, -- can be changed by right-clicking on player display_interaction_points = true, -- can be changed by right-clicking on player display_throw_info = true, display_blocked_status = true, display_sprite_info = true, display_sprite_hitbox = true, -- you still have to select the sprite with the mouse display_extended_sprite_info = false, display_cluster_sprite_info = false, display_minor_extended_sprite_info = false, display_bounce_sprite_info = false, display_level_info = true, display_yoshi_info = true, display_counters = true, display_controller_input = true, display_static_camera_region = false, -- shows the region in which the camera won't scroll horizontally draw_tiles_with_click = true, -- Some extra/debug info display_debug_info = false, -- shows useful info while investigating the game, but not very useful while TASing display_debug_player_extra = true, display_debug_sprite_extra = true, display_debug_sprite_tweakers = true, display_debug_extended_sprite = true, display_debug_cluster_sprite = true, display_debug_minor_extended_sprite = true, display_debug_bounce_sprite = true, display_debug_controller_data = false, -- Snes9x: might cause desyncs display_miscellaneous_sprite_table = false, miscellaneous_sprite_table_number = {[1] = true, [2] = true, [3] = true, [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true, [12] = true, [13] = true, [14] = true, [15] = true, [16] = true, [17] = true, [18] = true, [19] = true }, display_mouse_coordinates = false, draw_tile_map_grid = false, draw_tile_map_type = false, draw_tile_map_gfx_type = false, draw_tile_map_phy_type = false, -- Memory edit function address = 0x7E0000, size = 1, value = 0, edit_method = "Poke", edit_sprite_table = false, edit_sprite_table_number = {[1] = false, [2] = false, [3] = false, [4] = false, [5] = false, [6] = false, [7] = false, [8] = false, [9] = false, [10] = false, [11] = false, [12] = false, [13] = false, [14] = false, [15] = false, [16] = false, [17] = false, [18] = false, [19] = false, [20] = false, [21] = false, [22] = false, [23] = false, [24] = false}, -- Script settings max_tiles_drawn = 20, -- the max number of tiles to be drawn/registered by the script } -- Colour settings local DEFAULT_COLOUR = { -- Text default_text_opacity = 1.0, default_bg_opacity = 0.7, text = "#ffffff", -- white background = "#000000", -- black halo = "#000040", positive = "#00ff00", -- green warning = "#ff0000", -- red warning_bg = "#000000ff", warning2 = "#ff00ff", -- purple warning_soft = "#ffa500", -- orange weak = "#00a9a9a9", weak2 = "#555555", -- gray very_weak = "#a0ffffff", memory = "#00ffff", -- cyan joystick_input = "#ffff00ff", joystick_input_bg = "#ffffff30", button_text = "#300030ff", mainmenu_outline = "#ffffffc0", mainmenu_bg = "#000000c0", -- Counters counter_pipe = "#00ff00ff", counter_multicoin = "#ffff00ff", counter_gray_pow = "#a5a5a5ff", counter_blue_pow = "#4242deff", counter_dircoin = "#8c5a19ff", counter_pballoon = "#f8d870ff", counter_star = "#ffd773ff", counter_fireflower = "#ff8c00ff", -- hitbox and related text mario = "#ff0000ff", mario_bg = "#00000000", mario_mounted_bg = "#00000000", interaction = "#ffffffff", interaction_bg = "#00000020", interaction_nohitbox = "#000000a0", interaction_nohitbox_bg = "#00000070", sprites = { "#80ffff", -- cyan "#a0a0ff", -- blue "#ff6060", -- red "#ff80ff", -- magenta "#ffa100", -- orange "#ffff80", -- yellow "#40ff40" -- green }, sprites_bg = "#00ff00", sprites_interaction_pts = "#ffffffff", sprites_clipping_bg = "#000000a0", extended_sprites = { "#ff3700", -- orange to red gradient 6 "#ff5b00", -- orange to red gradient 4 "#ff8000", -- orange to red gradient 2 "#ffa500" -- orange to red gradient 0 (orange) }, extended_sprites_bg = "#00ff0050", special_extended_sprite_bg = "#00ff0060", goal_tape_bg = "#ffff0050", fireball = "#b0d0ffff", baseball = "#0040a0ff", cluster_sprites = "#ff80a0ff", sumo_brother_flame = "#0040a0ff", minor_extended_sprites = "#ff90b0ff", awkward_hitbox = "#204060ff", awkward_hitbox_bg = "#ff800060", yoshi = "#00ffffff", yoshi_bg = "#00ffff40", yoshi_mounted_bg = "#00000000", tongue_line = "#ffa000ff", tongue_bg = "#00000060", cape = "#ffd700ff", cape_bg = "#ffd70060", block = "#00008bff", blank_tile = "#ffffff70", block_bg = "#22cc88a0", layer2_line = "#ff2060ff", layer2_bg = "#ff206040", static_camera_region = "#40002040", } -- Font settings local SNES9X_FONT_HEIGHT = 8 local SNES9X_FONT_WIDTH = 4 -- GD images dumps (encoded) local GD_IMAGES_DUMPS = {} GD_IMAGES_DUMPS.player_blocked_status = {255, 254, 0, 25, 0, 30, 1, 255, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 66, 33, 0, 255, 148, 0, 0, 255, 148, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 255, 66, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 99, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 99, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 2, 0, 0, 0, 0, 255, 148, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 255, 255, 255, 0, 0, 255, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 255, 0, 0, 0, 99, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 255, 66, 33, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 255, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 148, 49, 33, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 255, 198, 173, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 198, 173, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 173, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 66, 33, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 255, 198, 173, 0, 255, 198, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 255, 198, 173, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 66, 33, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 255, 198, 173, 0, 255, 198, 173, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 173, 0, 0, 255, 198, 173, 0, 255, 198, 173, 0, 255, 198, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 66, 33, 0, 255, 255, 255, 0, 255, 66, 33, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 173, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 66, 33, 0, 255, 66, 33, 0, 255, 66, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 231, 214, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 173, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 255, 231, 214, 0, 255, 231, 214, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 231, 214, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 173, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 231, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 198, 173, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 255, 66, 33, 0, 255, 148, 0, 0, 255, 148, 0, 0, 0, 0, 0, 0, 255, 198, 173, 0, 255, 198, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 255, 66, 33, 0, 255, 148, 0, 0, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 148, 49, 33, 0, 148, 49, 33, 0, 255, 66, 33, 0, 255, 255, 255, 0, 255, 148, 0, 0, 0, 0, 0, 0, 148, 49, 33, 0, 255, 66, 33, 0, 255, 148, 0, 0, 255, 148, 0, 0, 255, 255, 255, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 148, 49, 33, 0, 148, 49, 33, 0, 255, 66, 33, 0, 255, 148, 0, 0, 255, 148, 0, 0, 255, 148, 0, 0, 0, 0, 0, 0, 148, 49, 33, 0, 148, 49, 33, 0, 255, 66, 33, 0, 255, 66, 33, 0, 255, 148, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255} GD_IMAGES_DUMPS.goal_tape = {255, 254, 0, 18, 0, 6, 1, 255, 255, 255, 255, 107, 153, 153, 153, 38, 75, 75, 75, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 0, 63, 63, 63, 32, 84, 84, 84, 0, 186, 186, 186, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 62, 62, 62, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 248, 248, 248, 0, 55, 55, 55, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 0, 216, 216, 216, 33, 75, 75, 75, 0, 136, 136, 136, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 0, 176, 176, 176, 106, 160, 160, 160, 40, 60, 60, 60, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40, 0, 40, 40, 40} -- Symbols local LEFT_ARROW = "<-" local RIGHT_ARROW = "->" -- Others local Border_right, Border_left, Border_top, Border_bottom = 0, 0, 0, 0 local Buffer_width, Buffer_height, Buffer_middle_x, Buffer_middle_y = 256, 224, 128, 112 local Screen_width, Screen_height, AR_x, AR_y = 256, 224, 1, 1 local Y_CAMERA_OFF = 1 -- small adjustment to display the tiles according to their actual graphics -- Input key names local INPUT_KEYNAMES = { -- Snes9x xmouse=0, ymouse=0, leftclick=false, rightclick=false, middleclick=false, shift=false, control=false, alt=false, capslock=false, numlock=false, scrolllock=false, ["false"]=false, ["1"]=false, ["2"]=false, ["3"]=false, ["4"]=false, ["5"]=false, ["6"]=false, ["7"]=false, ["8"]=false,["9"]=false, A=false, B=false, C=false, D=false, E=false, F=false, G=false, H=false, I=false, J=false, K=false, L=false, M=false, N=false, O=false, P=false, Q=false, R=false, S=false, T=false, U=false, V=false, W=false, X=false, Y=false, Z=false, F1=false, F2=false, F3=false, F4=false, F5=false, F6=false, F7=false, F8=false, F9=false, F1false=false, F11=false, F12=false, F13=false, F14=false, F15=false, F16=false, F17=false, F18=false, F19=false, F2false=false, F21=false, F22=false, F23=false, F24=false, backspace=false, tab=false, enter=false, pause=false, escape=false, space=false, pageup=false, pagedown=false, ["end"]=false, home=false, insert=false, delete=false, left=false, up=false, right=false, down=false, numpadfalse=false, numpad1=false, numpad2=false, numpad3=false, numpad4=false, numpad5=false, numpad6=false, numpad7=false, numpad8=false, numpad9=false, ["numpad*"]=false, ["numpad+"]=false, ["numpad-"]=false, ["numpad."]=false, ["numpad/"]=false, tilde=false, plus=false, minus=false, leftbracket=false, rightbracket=false, semicolon=false, quote=false, comma=false, period=false, slash=false, backslash=false } -- END OF CONFIG < < < < < < < --############################################################################# -- INITIAL STATEMENTS: print("Starting script") -- Load environment local gui, input, joypad, emu, movie, memory = gui, input, joypad, emu, movie, memory local unpack = unpack or table.unpack local string, math, table, next, ipairs, pairs, io, os, type = string, math, table, next, ipairs, pairs, io, os, type local bit = require"bit" -- Script tries to verify whether the emulator is indeed Snes9x-rr if snes9x == nil then error("This script works with Snes9x-rr emulator.") end -- TEST: INI library for handling an ini configuration file function file_exists(name) local f = io.open(name, "r") if f ~= nil then io.close(f) return true else return false end end function copytable(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[copytable(orig_key)] = copytable(orig_value) -- possible stack overflow end setmetatable(copy, copytable(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end function mergetable(source, t2) for key, value in pairs(t2) do if type(value) == "table" then if type(source[key] or false) == "table" then mergetable(source[key] or {}, t2[key] or {}) -- possible stack overflow else source[key] = value end else source[key] = value end end return source end -- Creates a set from a list local function make_set(list) local set = {} for _, l in ipairs(list) do set[l] = true end return set end local INI = {} function INI.arg_to_string(value) local str if type(value) == "string" then str = "\"" .. value .. "\"" elseif type(value) == "number" or type(value) == "boolean" or value == nil then str = tostring(value) elseif type(value) == "table" then local tmp = {"{"} -- only arrays for a, b in ipairs(value) do table.insert(tmp, ("%s%s"):format(INI.arg_to_string(b), a ~= #value and ", " or "")) -- possible stack overflow end table.insert(tmp, "}") str = table.concat(tmp) else str = "#BAD_VALUE" end return str end -- creates the string for ini function INI.data_to_string(data) local sections = {} for section, prop in pairs(data) do local properties = {} for key, value in pairs(prop) do table.insert(properties, ("%s = %s\n"):format(key, INI.arg_to_string(value))) -- properties end table.sort(properties) table.insert(sections, ("[%s]\n"):format(section) .. table.concat(properties) .. "\n") end table.sort(sections) return table.concat(sections) end function INI.string_to_data(value) local data if tonumber(value) then data = tonumber(value) elseif value == "true" then data = true elseif value == "false" then data = false elseif value == "nil" then data = nil else local quote1, text, quote2 = value:match("(['\"{])(.+)(['\"}])") -- value is surrounded by "", '' or {}? if quote1 and quote2 and text then if (quote1 == '"' or quote1 == "'") and quote1 == quote2 then data = text elseif quote1 == "{" and quote2 == "}" then local tmp = {} -- test for words in text:gmatch("[^,%s]+") do tmp[#tmp + 1] = INI.string_to_data(words) -- possible stack overflow end data = tmp else data = value end else data = value end end return data end function INI.load(filename) local file = io.open(filename, "r") if not file then return false end local data, section = {}, nil for line in file:lines() do local new_section = line:match("^%[([^%[%]]+)%]$") if new_section then section = INI.string_to_data(new_section) and INI.string_to_data(new_section) or new_section if data[section] then print("Duplicated section") end data[section] = data[section] or {} else local prop, value = line:match("^([%w_%-%.]+)%s*=%s*(.+)%s*$") -- prop = value if prop and value then value = INI.string_to_data(value) prop = INI.string_to_data(prop) and INI.string_to_data(prop) or prop if data[section] == nil then print(prop, value) ; error("Property outside section") end data[section][prop] = value else local ignore = line:match("^;") or line == "" if not ignore then print("BAD LINE:", line, prop, value) end end end end file:close() return data end function INI.retrieve(filename, data) if type(data) ~= "table" then error"data must be a table" end local previous_data -- Verifies if file already exists if file_exists(filename) then ini_data = INI.load(filename) else return data end -- Adds previous values to the new ini local union_data = mergetable(data, ini_data) return union_data end function INI.overwrite(filename, data) local file, err = assert(io.open(filename, "w"), "Error loading file :" .. filename) if not file then print(err) ; return end file:write(INI.data_to_string(data)) file:close() end function INI.save(filename, data) if type(data) ~= "table" then error"data must be a table" end local tmp, previous_data if file_exists(filename) then previous_data = INI.load(filename) tmp = mergetable(previous_data, data) else tmp = data end INI.overwrite(filename, tmp) end local OPTIONS = file_exists(INI_CONFIG_FILENAME) and INI.retrieve(INI_CONFIG_FILENAME, {["SNES9X OPTIONS"] = DEFAULT_OPTIONS}).OPTIONS or DEFAULT_OPTIONS local COLOUR = file_exists(INI_CONFIG_FILENAME) and INI.retrieve(INI_CONFIG_FILENAME, {["SNES9X COLOURS"] = DEFAULT_COLOUR}).COLOURS or DEFAULT_COLOUR INI.save(INI_CONFIG_FILENAME, {["SNES9X COLOURS"] = COLOUR}) -- Snes9x doesn't need to convert colour string to number INI.save(INI_CONFIG_FILENAME, {["SNES9X OPTIONS"] = OPTIONS}) function INI.save_options() INI.save(INI_CONFIG_FILENAME, {["SNES9X OPTIONS"] = OPTIONS}) end --######################## -- end of test -- Text/Background_max_opacity is only changed by the player using the hotkeys -- Text/Bg_opacity must be used locally inside the functions local Text_max_opacity = COLOUR.default_text_opacity local Background_max_opacity = COLOUR.default_bg_opacity local Text_opacity = 1 local Bg_opacity = 1 local fmt = string.format local floor = math.floor local sqrt = math.sqrt local sin = math.sin local cos = math.cos local pi = math.pi -- unsigned to signed (based in bits) local function signed(num, bits) local maxval = 2^(bits - 1) if num < maxval then return num else return num - 2*maxval end end -- Compatibility of the memory read/write functions local u8 = memory.readbyte local s8 = memory.readbytesigned local w8 = memory.writebyte local u16 = memory.readword local s16 = memory.readwordsigned local w16 = memory.writeword local u24 = function(address, value) return 256*u16(address + 1) + u8(address) end local s24 = function(address, value) return signed(256*u16(address + 1) + u8(address), 24) end local w24 = function(address, value) w16(address + 1, floor(value/256)) ; w8(address, value%256) end -- Images (for gd library) local IMAGES = {} IMAGES.player_blocked_status = string.char(unpack(GD_IMAGES_DUMPS.player_blocked_status)) IMAGES.goal_tape = string.char(unpack(GD_IMAGES_DUMPS.goal_tape)) -- Hotkeys availability if INPUT_KEYNAMES[OPTIONS.hotkey_increase_opacity] == nil then print(string.format("Hotkey '%s' is not available, to increase opacity.", OPTIONS.hotkey_increase_opacity)) else print(string.format("Hotkey '%s' set to increase opacity.", OPTIONS.hotkey_increase_opacity)) end if INPUT_KEYNAMES[OPTIONS.hotkey_decrease_opacity] == nil then print(string.format("Hotkey '%s' is not available, to decrease opacity.", OPTIONS.hotkey_decrease_opacity)) else print(string.format("Hotkey '%s' set to decrease opacity.", OPTIONS.hotkey_decrease_opacity)) end --############################################################################# -- GAME AND SNES SPECIFIC MACROS: local NTSC_FRAMERATE = 60.0 local SMW2 = { -- Game Modes game_mode_overworld = 0x0022, game_mode_level = 0x000F, -- Sprites sprite_max = 24, extended_sprite_max = 16, cluster_sprite_max = 20, minor_extended_sprite_max = 12, bounce_sprite_max = 4, null_sprite_id = 0xff, -- Blocks blank_tile_map16 = 0x25, } SFXRAM = { -- 700000~701FFF -- General level_timer = 0x701974, -- 2 bytes screen_number_to_id = 0x700CAA, -- 128 bytes table RNG = 0x701970, -- 2 bytes -- Player x = 0x70008C, -- 2 bytes y = 0x700090, -- 2 bytes --previous_x = 0x7000d1, --previous_y = 0x7000d3, x_sub = 0x70008A, y_sub = 0x70008E, x_speed = 0x7000A9, x_subspeed = 0x7000A8, y_speed = 0x7000AB, y_subspeed = 0x7000AA, direction = 0x7000C4, ground_pound_state = 0x7000D4, ground_pound_timer = 0x7000D6, egg_target_x = 0x7000E4, -- 2 bytes egg_target_y = 0x7000E6, -- 2 bytes egg_target_radial_pos = 0x7000EF, egg_target_radial_subpos = 0x7000EE, egg_throw_state = 0x7000DE, egg_throw_state_timer = 0x7001E2, player_blocked_status = 0x7000FC, x_centered = 0x70011C, -- 2 bytes y_centered = 0x70011E, -- 2 bytes tongue_x = 0x700152, -- 2 bytes tongue_y = 0x700154, -- 2 bytes tongue_state = 0x700150, ammo_in_mouth = 0x70016A, is_frozen = 0x7001AE, -- 2 bytes on_sprite_platform = 0x7001B4, --is_ducking = 0x700073, --p_meter = 0x7013e4, --take_off = 0x70149f, --powerup = 0x700019, --diving_status = 0x701409, --player_animation_trigger = 0x700071, --climbing_status = 0x700074, --on_ground = 0x7013ef, --on_ground_delay = 0x70008d, --on_air = 0x700072, --can_jump_from_water = 0x7013fa, --carrying_item = 0x70148f, --player_looking_up = 0x7013de, -- Baby Mario mario_status = 0x700F00, -- Timer invincibility_timer = 0x7001D6, eat_timer = 0x7001EE, transform_timer = 0x7001F4, star_timer = 0x701E04, -- Sprites sprite_status = 0x700F00, sprite_type = 0x701360, sprite_x = 0x7010E0, -- 2 bytes sprite_y = 0x701180, -- 2 bytes sprite_x_sub = 0x7010DF, sprite_y_sub = 0x70117F, sprite_x_speed = 0x701221, sprite_x_subspeed = 0x701220, sprite_y_speed = 0x701223, sprite_y_subspeed = 0x701222, sprite_hitbox_half_width = 0x701B56, -- 2 bytes sprite_hitbox_half_height = 0x701B58, -- 2 bytes sprite_x_center = 0x701CD6, -- 2 bytes sprite_y_center = 0x701CD8 -- 2 bytes } --[[ for name, address in pairs(SFXRAM) do address = address + 0x700000 -- Snes9x end]] local SFXRAM = SFXRAM WRAM = { -- 7E0000~7FFFFF -- I/O ctrl_1_1 = 0x0015, ctrl_1_2 = 0x0017, firstctrl_1_1 = 0x0016, firstctrl_1_2 = 0x0018, -- General game_mode = 0x0118, --real_frame = 0x0013, --effective_frame = 0x0014, --lag_indicator = 0x01fe, --timer_frame_counter = 0x0f30, --RNG = 0x148d, --current_level = 0x00fe, -- plus 1 --lock_animation_flag = 0x009d, -- Most codes will still run if this is set, but almost nothing will move or animate. --level_mode_settings = 0x1925, red_coin_counter = 0x03B4, star_counter = 0x03B6, flower_counter = 0x03B8, coin_counter = 0x037b, is_paused = 0x0B10, Map16_data = 0x18000, -- 32768 bytes table, in words -- Cheats frozen = 0x13fb, level_paused = 0x13d4, level_index = 0x021A, -- 2 bytes room_index = 0x00ce, level_flag_table = 0x1ea2, level_exit_type = 0x0dd5, midway_point = 0x13ce, -- Camera camera_x = 0x0039, camera_y = 0x003B, screens_number = 0x005d, hscreen_number = 0x005e, vscreen_number = 0x005f, vertical_scroll_flag_header = 0x1412, -- #$00 = Disable; #$01 = Enable; #$02 = Enable if flying/climbing/etc. vertical_scroll_enabled = 0x13f1, camera_scroll_timer = 0x1401, -- Sprites sprite_status = 0x14c8, sprite_number = 0x009e, sprite_x_high = 0x14e0, sprite_x_low = 0x00e4, sprite_y_high = 0x14d4, sprite_y_low = 0x00d8, sprite_x_sub = 0x14f8, sprite_y_sub = 0x14ec, sprite_x_speed = 0x00b6, sprite_y_speed = 0x00aa, sprite_x_offscreen = 0x15a0, sprite_y_offscreen = 0x186c, sprite_miscellaneous1 = 0x00c2, sprite_miscellaneous2 = 0x1504, sprite_miscellaneous3 = 0x1510, sprite_miscellaneous4 = 0x151c, sprite_miscellaneous5 = 0x1528, sprite_miscellaneous6 = 0x1534, sprite_miscellaneous7 = 0x1540, sprite_miscellaneous8 = 0x154c, sprite_miscellaneous9 = 0x1558, sprite_miscellaneous10 = 0x1564, sprite_miscellaneous11 = 0x1570, sprite_miscellaneous12 = 0x157c, sprite_miscellaneous13 = 0x1594, sprite_miscellaneous14 = 0x15ac, sprite_miscellaneous15 = 0x1602, sprite_miscellaneous16 = 0x160e, sprite_miscellaneous17 = 0x1626, sprite_miscellaneous18 = 0x163e, sprite_miscellaneous19 = 0x187b, sprite_underwater = 0x164a, sprite_disable_cape = 0x1fe2, sprite_1_tweaker = 0x1656, sprite_2_tweaker = 0x1662, sprite_3_tweaker = 0x166e, sprite_4_tweaker = 0x167a, sprite_5_tweaker = 0x1686, sprite_6_tweaker = 0x190f, sprite_tongue_wait = 0x14a3, sprite_yoshi_squatting = 0x18af, sprite_buoyancy = 0x190e, toadies_relative_x = 0x0E38, toadies_relative_y = 0x0E4A, -- Extended sprites extspr_number = 0x170b, extspr_x_high = 0x1733, extspr_x_low = 0x171f, extspr_y_high = 0x1729, extspr_y_low = 0x1715, extspr_x_speed = 0x1747, extspr_y_speed = 0x173d, extspr_suby = 0x1751, extspr_subx = 0x175b, extspr_table = 0x1765, extspr_table2 = 0x176f, -- Cluster sprites cluspr_flag = 0x18b8, cluspr_number = 0x1892, cluspr_x_high = 0x1e3e, cluspr_x_low = 0x1e16, cluspr_y_high = 0x1e2a, cluspr_y_low = 0x1e02, cluspr_timer = 0x0f9a, cluspr_table_1 = 0x0f4a, cluspr_table_2 = 0x0f72, cluspr_table_3 = 0x0f86, reappearing_boo_counter = 0x190a, -- Minor extended sprites minorspr_number = 0x17f0, minorspr_x_high = 0x18ea, minorspr_x_low = 0x1808, minorspr_y_high = 0x1814, minorspr_y_low = 0x17fc, minorspr_xspeed = 0x182c, minorspr_yspeed = 0x1820, minorspr_x_sub = 0x1844, minorspr_y_sub = 0x1838, minorspr_timer = 0x1850, -- Bounce sprites bouncespr_number = 0x1699, bouncespr_x_high = 0x16ad, bouncespr_x_low = 0x16a5, bouncespr_y_high = 0x16a9, bouncespr_y_low = 0x16a1, bouncespr_timer = 0x16c5, bouncespr_last_id = 0x18cd, turn_block_timer = 0x18ce, -- Player x = 0x0094, y = 0x0096, previous_x = 0x00d1, previous_y = 0x00d3, x_sub = 0x13da, y_sub = 0x13dc, x_speed = 0x007b, x_subspeed = 0x007a, y_speed = 0x007d, direction = 0x0076, is_ducking = 0x0073, p_meter = 0x13e4, take_off = 0x149f, powerup = 0x0019, cape_spin = 0x14a6, cape_fall = 0x14a5, cape_interaction = 0x13e8, flight_animation = 0x1407, diving_status = 0x1409, player_animation_trigger = 0x0071, climbing_status = 0x0074, spinjump_flag = 0x140d, player_blocked_status = 0x0077, player_item = 0x0dc2, --hex cape_x = 0x13e9, cape_y = 0x13eb, on_ground = 0x13ef, on_ground_delay = 0x008d, on_air = 0x0072, can_jump_from_water = 0x13fa, carrying_item = 0x148f, mario_score = 0x0f34, player_coin = 0x0dbf, player_looking_up = 0x13de, -- Yoshi yoshi_riding_flag = 0x187a, -- #$00 = No, #$01 = Yes, #$02 = Yes, and turning around. yoshi_tile_pos = 0x0d8c, -- Timers --pipe_entrance_timer = 0x0088, end_level_timer = 0x1493, --multicoin_block_timer = 0x186b,, switch_timer = 0x0CEC, -- 2 bytes -- Layers layer2_x_nextframe = 0x1466, layer2_y_nextframe = 0x1468, } for name, address in pairs(WRAM) do address = address + 0x7e0000 -- Snes9x end local WRAM = WRAM local SOLID_BLOCKS = { -- solid and one-way solid blocks, via tests 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x0A, 0x0C, 0x0D, 0x0F, 0x10, 0x15, 0x1A, 0x1B, 0x1C , 0x29, 0x2C, 0x2F, 0x33, 0x38, 0x39, 0x3E, 0x3F, 0x40, 0x41, 0x44, 0x45, 0x48, 0x49, 0x4B, 0x4C, 0x4E, 0x50, 0x53, 0x55, 0x57, 0x59, 0x5B, 0x5D, 0x5F, 0x66, 0x67, 0x6B, 0x6E, 0x79, 0x7D, 0x90, 0x95, 0x9A, 0x9D, 0x9F, 0xA0, 0xA1, 0xA2 } local X_INTERACTION_POINTS = {center = 0x8, left_side = 0x2 + 1, left_foot = 0x3, right_side = 0xe - 1, right_foot = 0xd} local Y_INTERACTION_POINTS = { {head = 0x10, center = 0x18, shoulder = 0x16, side = 0x1f, foot = 0x20, sprite = 0x15}, {head = 0x08, center = 0x12, shoulder = 0x0f, side = 0x1f, foot = 0x20, sprite = 0x07}, -- this one, for now {head = 0x13, center = 0x1d, shoulder = 0x19, side = 0x28, foot = 0x30, sprite = 0x19}, {head = 0x10, center = 0x1a, shoulder = 0x16, side = 0x28, foot = 0x30, sprite = 0x11} } local HITBOX_SPRITE = { -- sprites' hitbox against player and other sprites [0x00] = { xoff = 2, yoff = 3, width = 12, height = 10, oscillation = true }, [0x01] = { xoff = 2, yoff = 3, width = 12, height = 21, oscillation = true }, [0x02] = { xoff = 16, yoff = -2, width = 16, height = 18, oscillation = true }, [0x03] = { xoff = 20, yoff = 8, width = 8, height = 8, oscillation = true }, [0x04] = { xoff = 0, yoff = -2, width = 48, height = 14, oscillation = true }, [0x05] = { xoff = 0, yoff = -2, width = 80, height = 14, oscillation = true }, [0x06] = { xoff = 1, yoff = 2, width = 14, height = 24, oscillation = true }, [0x07] = { xoff = 8, yoff = 8, width = 40, height = 48, oscillation = true }, [0x08] = { xoff = -8, yoff = -2, width = 32, height = 16, oscillation = true }, [0x09] = { xoff = -2, yoff = 8, width = 20, height = 30, oscillation = true }, [0x0a] = { xoff = 3, yoff = 7, width = 1, height = 2, oscillation = true }, [0x0b] = { xoff = 6, yoff = 6, width = 3, height = 3, oscillation = true }, [0x0c] = { xoff = 1, yoff = -2, width = 13, height = 22, oscillation = true }, [0x0d] = { xoff = 0, yoff = -4, width = 15, height = 16, oscillation = true }, [0x0e] = { xoff = 6, yoff = 6, width = 20, height = 20, oscillation = true }, [0x0f] = { xoff = 2, yoff = -2, width = 36, height = 18, oscillation = true }, [0x10] = { xoff = 0, yoff = -2, width = 15, height = 32, oscillation = true }, [0x11] = { xoff = -24, yoff = -24, width = 64, height = 64, oscillation = true }, [0x12] = { xoff = -4, yoff = 16, width = 8, height = 52, oscillation = true }, [0x13] = { xoff = -4, yoff = 16, width = 8, height = 116, oscillation = true }, [0x14] = { xoff = 4, yoff = 2, width = 24, height = 12, oscillation = true }, [0x15] = { xoff = 0, yoff = -2, width = 15, height = 14, oscillation = true }, [0x16] = { xoff = -4, yoff = -12, width = 24, height = 24, oscillation = true }, [0x17] = { xoff = 2, yoff = 8, width = 12, height = 69, oscillation = true }, [0x18] = { xoff = 2, yoff = 19, width = 12, height = 58, oscillation = true }, [0x19] = { xoff = 2, yoff = 35, width = 12, height = 42, oscillation = true }, [0x1a] = { xoff = 2, yoff = 51, width = 12, height = 26, oscillation = true }, [0x1b] = { xoff = 2, yoff = 67, width = 12, height = 10, oscillation = true }, [0x1c] = { xoff = 0, yoff = 10, width = 10, height = 48, oscillation = true }, [0x1d] = { xoff = 2, yoff = -3, width = 28, height = 27, oscillation = true }, [0x1e] = { xoff = 6, yoff = -8, width = 3, height = 32, oscillation = true }, -- default: { xoff = -32, yoff = -8, width = 48, height = 32, oscillation = true }, [0x1f] = { xoff = -16, yoff = -4, width = 48, height = 18, oscillation = true }, [0x20] = { xoff = -4, yoff = -24, width = 8, height = 24, oscillation = true }, [0x21] = { xoff = -4, yoff = 16, width = 8, height = 24, oscillation = true }, [0x22] = { xoff = 0, yoff = 0, width = 16, height = 16, oscillation = true }, [0x23] = { xoff = -8, yoff = -24, width = 32, height = 32, oscillation = true }, [0x24] = { xoff = -12, yoff = 32, width = 56, height = 56, oscillation = true }, [0x25] = { xoff = -14, yoff = 4, width = 60, height = 20, oscillation = true }, [0x26] = { xoff = 0, yoff = 88, width = 32, height = 8, oscillation = true }, [0x27] = { xoff = -4, yoff = -4, width = 24, height = 24, oscillation = true }, [0x28] = { xoff = -14, yoff = -24, width = 28, height = 40, oscillation = true }, [0x29] = { xoff = -16, yoff = -4, width = 32, height = 27, oscillation = true }, [0x2a] = { xoff = 2, yoff = -8, width = 12, height = 19, oscillation = true }, [0x2b] = { xoff = 0, yoff = 2, width = 16, height = 76, oscillation = true }, [0x2c] = { xoff = -8, yoff = -8, width = 16, height = 16, oscillation = true }, [0x2d] = { xoff = 4, yoff = 4, width = 8, height = 4, oscillation = true }, [0x2e] = { xoff = 2, yoff = -2, width = 28, height = 34, oscillation = true }, [0x2f] = { xoff = 2, yoff = -2, width = 28, height = 32, oscillation = true }, [0x30] = { xoff = 8, yoff = -14, width = 16, height = 28, oscillation = true }, [0x31] = { xoff = 0, yoff = -2, width = 48, height = 18, oscillation = true }, [0x32] = { xoff = 0, yoff = -2, width = 48, height = 18, oscillation = true }, [0x33] = { xoff = 0, yoff = -2, width = 64, height = 18, oscillation = true }, [0x34] = { xoff = -4, yoff = -4, width = 8, height = 8, oscillation = true }, [0x35] = { xoff = 3, yoff = 0, width = 18, height = 32, oscillation = true }, [0x36] = { xoff = 8, yoff = 8, width = 52, height = 46, oscillation = true }, [0x37] = { xoff = 0, yoff = -8, width = 15, height = 20, oscillation = true }, [0x38] = { xoff = 8, yoff = 16, width = 32, height = 40, oscillation = true }, [0x39] = { xoff = 4, yoff = 3, width = 8, height = 10, oscillation = true }, [0x3a] = { xoff = -8, yoff = 16, width = 32, height = 16, oscillation = true }, [0x3b] = { xoff = 0, yoff = 0, width = 16, height = 13, oscillation = true }, [0x3c] = { xoff = 12, yoff = 10, width = 3, height = 6, oscillation = true }, [0x3d] = { xoff = 12, yoff = 21, width = 3, height = 20, oscillation = true }, [0x3e] = { xoff = 16, yoff = 18, width = 254, height = 16, oscillation = true }, [0x3f] = { xoff = 8, yoff = 8, width = 8, height = 24, oscillation = true } } local OBJ_CLIPPING_SPRITE = { -- sprites' interaction points against objects [0x0] = {xright = 14, xleft = 2, xdown = 8, xup = 8, yright = 8, yleft = 8, ydown = 16, yup = 2}, [0x1] = {xright = 14, xleft = 2, xdown = 7, xup = 7, yright = 18, yleft = 18, ydown = 32, yup = 2}, [0x2] = {xright = 7, xleft = 7, xdown = 7, xup = 7, yright = 7, yleft = 7, ydown = 7, yup = 7}, [0x3] = {xright = 14, xleft = 2, xdown = 8, xup = 8, yright = 16, yleft = 16, ydown = 32, yup = 11}, [0x4] = {xright = 16, xleft = 0, xdown = 8, xup = 8, yright = 18, yleft = 18, ydown = 32, yup = 2}, [0x5] = {xright = 13, xleft = 2, xdown = 8, xup = 8, yright = 24, yleft = 24, ydown = 32, yup = 16}, [0x6] = {xright = 7, xleft = 0, xdown = 4, xup = 4, yright = 4, yleft = 4, ydown = 8, yup = 0}, [0x7] = {xright = 31, xleft = 1, xdown = 16, xup = 16, yright = 16, yleft = 16, ydown = 31, yup = 1}, [0x8] = {xright = 15, xleft = 0, xdown = 8, xup = 8, yright = 8, yleft = 8, ydown = 15, yup = 0}, [0x9] = {xright = 16, xleft = 0, xdown = 8, xup = 8, yright = 8, yleft = 8, ydown = 16, yup = 0}, [0xa] = {xright = 13, xleft = 2, xdown = 8, xup = 8, yright = 72, yleft = 72, ydown = 80, yup = 66}, [0xb] = {xright = 14, xleft = 2, xdown = 8, xup = 8, yright = 4, yleft = 4, ydown = 8, yup = 0}, [0xc] = {xright = 13, xleft = 2, xdown = 8, xup = 8, yright = 0, yleft = 0, ydown = 0, yup = 0}, [0xd] = {xright = 16, xleft = 0, xdown = 8, xup = 8, yright = 8, yleft = 8, ydown = 16, yup = 0}, [0xe] = {xright = 31, xleft = 0, xdown = 16, xup = 16, yright = 8, yleft = 8, ydown = 16, yup = 0}, [0xf] = {xright = 8, xleft = 8, xdown = 8, xup = 16, yright = 4, yleft = 1, ydown = 2, yup = 4} } local HITBOX_EXTENDED_SPRITE = { -- extended sprites' hitbox -- To fill the slots... --[0] ={ xoff = 3, yoff = 3, width = 64, height = 64}, -- Free slot [0x01] ={ xoff = 3, yoff = 3, width = 0, height = 0}, -- Puff of smoke with various objects [0x0e] ={ xoff = 3, yoff = 3, width = 0, height = 0}, -- Wiggler's flower [0x0f] ={ xoff = 3, yoff = 3, width = 0, height = 0}, -- Trail of smoke [0x10] ={ xoff = 3, yoff = 3, width = 0, height = 0}, -- Spinjump stars [0x12] ={ xoff = 3, yoff = 3, width = 0, height = 0}, -- Water bubble -- extracted from ROM: [0x02] = { xoff = 3, yoff = 3, width = 1, height = 1, color_line = COLOUR.fireball }, -- Reznor fireball [0x03] = { xoff = 3, yoff = 3, width = 1, height = 1, color_line = COLOUR.fireball}, -- Flame left by hopping flame [0x04] = { xoff = 4, yoff = 4, width = 8, height = 8}, -- Hammer [0x05] = { xoff = 3, yoff = 3, width = 1, height = 1, color_line = COLOUR.fireball }, -- Player fireball [0x06] = { xoff = 4, yoff = 4, width = 8, height = 8}, -- Bone from Dry Bones [0x07] = { xoff = 0, yoff = 0, width = 0, height = 0}, -- Lava splash [0x08] = { xoff = 0, yoff = 0, width = 0, height = 0}, -- Torpedo Ted shooter's arm [0x09] = { xoff = 0, yoff = 0, width = 15, height = 15}, -- Unknown flickering object [0x0a] = { xoff = 4, yoff = 2, width = 8, height = 12}, -- Coin from coin cloud game [0x0b] = { xoff = 3, yoff = 3, width = 1, height = 1, color_line = COLOUR.fireball }, -- Piranha Plant fireball [0x0c] = { xoff = 3, yoff = 3, width = 1, height = 1, color_line = COLOUR.fireball }, -- Lava Lotus's fiery objects [0x0d] = { xoff = 3, yoff = 3, width = 1, height = 1, color_line = COLOUR.baseball }, -- Baseball -- got experimentally: [0x11] = { xoff = -0x1, yoff = -0x4, width = 11, height = 19}, -- Yoshi fireballs } -- Creates a set from a list local function make_set(list) local set = {} for _, l in ipairs(list) do set[l] = true end return set end -- from sprite number, returns oscillation flag -- A sprite must be here iff it processes interaction with player every frame AND this bit is not working in the sprite_4_tweaker WRAM(0x167a) local OSCILLATION_SPRITES = make_set{0x0e, 0x21, 0x29, 0x35, 0x54, 0x74, 0x75, 0x76, 0x77, 0x78, 0x81, 0x83, 0x87} -- Sprites that have a custom hitbox drawing local ABNORMAL_HITBOX_SPRITES = make_set{0x62, 0x63, 0x6b, 0x6c} -- Sprites whose clipping interaction points usually matter local GOOD_SPRITES_CLIPPING = make_set{ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xf, 0x10, 0x11, 0x13, 0x14, 0x18, 0x1b, 0x1d, 0x1f, 0x20, 0x26, 0x27, 0x29, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x3d, 0x3e, 0x3f, 0x40, 0x46, 0x47, 0x48, 0x4d, 0x4e, 0x51, 0x53, 0x6e, 0x6f, 0x70, 0x80, 0x81, 0x86, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa1, 0xa2, 0xa5, 0xa6, 0xa7, 0xab, 0xb2, 0xb4, 0xbb, 0xbc, 0xbd, 0xbf, 0xc3, 0xda, 0xdb, 0xdc, 0xdd, 0xdf } --############################################################################# -- SCRIPT UTILITIES: -- Variables used in various functions local Cheat = {} -- family of cheat functions and variables local Previous = {} local User_input = INPUT_KEYNAMES -- Snes9x local Tiletable = {} local Joypad = {} local Layer1_tiles = {} local Layer2_tiles = {} local Is_lagged = nil local Options_menu = {show_menu = false, current_tab = "Show/hide options"} local Filter_opacity, Filter_color = 0, "#000000ff" -- Snes9x specifc / unlisted color local Mario_boost_indicator = nil local Show_player_point_position = false local Sprites_info = {} -- keeps track of useful sprite info that might be used outside the main sprite function local Sprite_hitbox = {} -- keeps track of what sprite slots must display the hitbox local Memory = {} -- family of memory edit functions and variables -- Initialization of some tables for i = 0, SMW2.sprite_max -1 do Sprites_info[i] = {} end for key = 0, SMW2.sprite_max - 1 do Sprite_hitbox[key] = {} for number = 0, 0xff do Sprite_hitbox[key][number] = {["sprite"] = true, ["block"] = GOOD_SPRITES_CLIPPING[number]} end end -- Sum of the digits of a integer local function sum_digits(number) local sum = 0 while number > 0 do sum = sum + number%10 number = floor(number*0.1) end return sum end -- Returns the exact chosen digit of a number from the left to the right, in a given base -- E.g.: read_digit(654321, 2, 10) -> 5; read_digit(0x4B7A, 3, 16) -> 7 local function read_digit(number, digit, base) --assert(type(number) == "number" and number >= 0 and number%1 == 0, "Enter an integer number > 0") --assert(type(digit) == "number" and digit > 0 and digit%1 == 0, "Enter an integer digit > 0") --assert(type(base) == "number" and base > 1 and base%1 == 0, "Enter an integer base > 1") local copy = number local digits_total = 0 while copy >= 1 do copy = math.floor(copy/base) digits_total = digits_total + 1 end if digit > digits_total then return false end local result = math.floor(number/base^(digits_total - digit)) return result%base end -- Transform the binary representation of base into a string -- For instance, if each bit of a number represents a char of base, then this function verifies what chars are on local function decode_bits(data, base) local i = 1 local size = base:len() local direct_concatenation = size <= 45 -- Performance: I found out that the .. operator is faster for 45 operations or less local result if direct_concatenation then result = "" for ch in base:gmatch(".") do if bit.test(data, size - i) then result = result .. ch else result = result .. " " end i = i + 1 end else result = {} for ch in base:gmatch(".") do if bit.test(data, size-i) then result[i] = ch else result[i] = " " end i = i + 1 end result = table.concat(result) end return result end function bit.test(value, bitnum) -- Snes9x return bit.rshift(value, bitnum)%2 == 1 end local function mouse_onregion(x1, y1, x2, y2) -- Reads external mouse coordinates local mouse_x = User_input.xmouse local mouse_y = User_input.ymouse -- From top-left to bottom-right if x2 < x1 then x1, x2 = x2, x1 end if y2 < y1 then y1, y2 = y2, y1 end if mouse_x >= x1 and mouse_x <= x2 and mouse_y >= y1 and mouse_y <= y2 then return true else return false end end -- Register a function to be executed on key press or release -- execution happens in the main loop local Keys = {} Keys.press = {} Keys.release = {} Keys.down, Keys.up, Keys.pressed, Keys.released = {}, {}, {}, {} function Keys.registerkeypress(key, fn) Keys.press[key] = fn end function Keys.registerkeyrelease(key, fn) Keys.release[key] = fn end -- A cross sign with pos and size gui.crosshair = gui.crosshair or function(x, y, size, color) gui.line(x - size, y, x + size, y, color) gui.line(x, y-size, x, y+size, color) end local Movie_active, Readonly, Framecount, Lagcount, Rerecords local Lastframe_emulated, Starting_subframe_last_frame, Size_last_frame, Final_subframe_last_frame local Nextframe, Starting_subframe_next_frame, Starting_subframe_next_frame, Final_subframe_next_frame local function snes9x_status() Movie_active = movie.active() -- Snes9x Readonly = movie.playing() -- Snes9x Framecount = movie.length() Lagcount = emu.lagcount() -- Snes9x Rerecords = movie.rerecordcount() -- Last frame info Lastframe_emulated = emu.framecount() -- Next frame info (only relevant in readonly mode) Nextframe = Lastframe_emulated + 1 end -- draw a pixel given (x,y) with SNES' pixel sizes local draw_pixel = gui.pixel -- draws a line given (x,y) and (x',y') with given scale and SNES' pixel thickness (whose scale is 2) -- EDIT local function draw_line(x1, y1, x2, y2, scale, color) -- Draw from top-left to bottom-right if x2 < x1 then x1, x2 = x2, x1 end if y2 < y1 then y1, y2 = y2, y1 end x1, y1, x2, y2 = scale*x1, scale*y1, scale*x2, scale*y2 gui.line(x1, y1, x2, y2, color) end -- draw an arrow given (x,y) and (x',y') local function draw_arrow(x1, y1, x2, y2, color, head) local angle = math.atan((y2-y1)/(x2-x1)) -- in radians -- Arrow head local head_size = head or 10 local angle1, angle2 = angle + math.pi/4, angle - math.pi/4 --0.785398163398, angle - 0.785398163398 -- 45° in radians local delta_x1, delta_y1 = floor(head_size*math.cos(angle1)), floor(head_size*math.sin(angle1)) local delta_x2, delta_y2 = floor(head_size*math.cos(angle2)), floor(head_size*math.sin(angle2)) local head1_x1, head1_y1 = x2, y2 local head1_x2, head1_y2 local head2_x1, head2_y1 = x2, y2 local head2_x2, head2_y2 if x1 < x2 then -- 1st and 4th quadrant head1_x2, head1_y2 = head1_x1 - delta_x1, head1_y1 - delta_y1 head2_x2, head2_y2 = head2_x1 - delta_x2, head2_y1 - delta_y2 elseif x1 == x2 then -- vertical arrow head1_x2, head1_y2 = head1_x1 - delta_x1, head1_y1 - delta_y1 head2_x2, head2_y2 = head2_x1 - delta_x2, head2_y1 - delta_y2 else head1_x2, head1_y2 = head1_x1 + delta_x1, head1_y1 + delta_y1 head2_x2, head2_y2 = head2_x1 + delta_x2, head2_y1 + delta_y2 end -- Draw gui.line(x1, y1, x2, y2, color) gui.line(head1_x1, head1_y1, head1_x2, head1_y2, color) gui.line(head2_x1, head2_y1, head2_x2, head2_y2, color) end -- draws a box given (x,y) and (x',y') with SNES' pixel sizes local draw_box = function(x1, y1, x2, y2, line, fill) gui.box(x1, y1, x2, y2, fill, line) end -- draws a rectangle given (x,y) and dimensions, with SNES' pixel sizes local draw_rectangle = function(x, y, w, h, line, fill) gui.box(x, y, x + w, y + h, fill, line) end -- Takes a position and dimensions of a rectangle and returns a new position if this rectangle has points outside the screen local function put_on_screen(x, y, width, height) local x_screen, y_screen width = width or 0 height = height or 0 if x < - Border_left then x_screen = - Border_left elseif x > Buffer_width + Border_right - width then x_screen = Buffer_width + Border_right - width else x_screen = x end if y < - Border_top then y_screen = - Border_top elseif y > Buffer_height + Border_bottom - height then y_screen = Buffer_height + Border_bottom - height else y_screen = y end return x_screen, y_screen end -- returns the (x, y) position to start the text and its length: -- number, number, number text_position(x, y, text, font_width, font_height[[[[, always_on_client], always_on_game], ref_x], ref_y]) -- x, y: the coordinates that the refereed point of the text must have -- text: a string, don't make it bigger than the buffer area width and don't include escape characters -- font_width, font_height: the sizes of the font -- always_on_client, always_on_game: boolean -- ref_x and ref_y: refer to the relative point of the text that must occupy the origin (x,y), from 0% to 100% -- for instance, if you want to display the middle of the text in (x, y), then use 0.5, 0.5 local function text_position(x, y, text, font_width, font_height, always_on_client, always_on_game, ref_x, ref_y) -- Reads external variables local border_left = 0 local border_right = 0 local border_top = 0 local border_bottom = 0 local buffer_width = 256 local buffer_height = 224 -- text processing local text_length = text and string.len(text)*font_width or font_width -- considering another objects, like bitmaps -- actual position, relative to game area origin x = (not ref_x and x) or (ref_x == 0 and x) or x - floor(text_length*ref_x) y = (not ref_y and y) or (ref_y == 0 and y) or y - floor(font_height*ref_y) -- adjustment needed if text is supposed to be on screen area local x_end = x + text_length local y_end = y + font_height if always_on_game then if x < 0 then x = 0 end if y < 0 then y = 0 end if x_end > buffer_width then x = buffer_width - text_length end if y_end > buffer_height then y = buffer_height - font_height end elseif always_on_client then if x < -border_left then x = -border_left end if y < -border_top then y = -border_top end if x_end > buffer_width + border_right then x = buffer_width + border_right - text_length end if y_end > buffer_height + border_bottom then y = buffer_height + border_bottom - font_height end end return x, y, text_length end -- Complex function for drawing, that uses text_position local function draw_text(x, y, text, ...) -- Reads external variables local font_name = Font or false local font_width = SNES9X_FONT_WIDTH local font_height = SNES9X_FONT_HEIGHT local bg_default_color = font_name and COLOUR.outline or COLOUR.background local text_color, bg_color, always_on_client, always_on_game, ref_x, ref_y local arg1, arg2, arg3, arg4, arg5, arg6 = ... if not arg1 or arg1 == true then text_color = COLOUR.text bg_color = bg_default_color always_on_client, always_on_game, ref_x, ref_y = arg1, arg2, arg3, arg4 elseif not arg2 or arg2 == true then text_color = arg1 bg_color = bg_default_color always_on_client, always_on_game, ref_x, ref_y = arg2, arg3, arg4, arg5 else text_color, bg_color = arg1, arg2 always_on_client, always_on_game, ref_x, ref_y = arg3, arg4, arg5, arg6 end local x_pos, y_pos, length = text_position(x, y, text, font_width, font_height, always_on_client, always_on_game, ref_x, ref_y) ; gui.opacity(Text_max_opacity * Text_opacity) gui.text(x_pos, y_pos, text, text_color, bg_color) gui.opacity(1.0) -- Snes9x return x_pos + length, y_pos + font_height, length end local function alert_text(x, y, text, text_color, bg_color, always_on_game, ref_x, ref_y) -- Reads external variables local font_width = SNES9X_FONT_WIDTH local font_height = SNES9X_FONT_HEIGHT local x_pos, y_pos, text_length = text_position(x, y, text, font_width, font_height, false, always_on_game, ref_x, ref_y) gui.opacity(Background_max_opacity * Bg_opacity) draw_rectangle(x_pos, y_pos, text_length - 1, font_height - 1, bg_color, bg_color) -- Snes9x gui.opacity(Text_max_opacity * Text_opacity) gui.text(x_pos, y_pos, text, text_color, 0) gui.opacity(1.0) end local function draw_over_text(x, y, value, base, color_base, color_value, color_bg, always_on_client, always_on_game, ref_x, ref_y) value = decode_bits(value, base) local x_end, y_end, length = draw_text(x, y, base, color_base, color_bg, always_on_client, always_on_game, ref_x, ref_y) gui.opacity(Text_max_opacity * Text_opacity) gui.text(x_end - length, y_end - SNES9X_FONT_HEIGHT, value, color_value or COLOUR.text) gui.opacity(1.0) return x_end, y_end, length end -- Returns frames-time conversion local function frame_time(frame) if not NTSC_FRAMERATE then error("NTSC_FRAMERATE undefined."); return end local total_seconds = frame/NTSC_FRAMERATE local hours = floor(total_seconds/3600) local tmp = total_seconds - 3600*hours local minutes = floor(tmp/60) tmp = tmp - 60*minutes local seconds = floor(tmp) local miliseconds = 1000* (total_seconds%1) if hours == 0 then hours = "" else hours = string.format("%d:", hours) end local str = string.format("%s%.2d:%.2d.%03.0f", hours, minutes, seconds, miliseconds) return str end -- Background opacity functions local function increase_opacity() if Text_max_opacity <= 0.9 then Text_max_opacity = Text_max_opacity + 0.1 else if Background_max_opacity <= 0.9 then Background_max_opacity = Background_max_opacity + 0.1 end end end local function decrease_opacity() if Background_max_opacity >= 0.1 then Background_max_opacity = Background_max_opacity - 0.1 else if Text_max_opacity >= 0.1 then Text_max_opacity = Text_max_opacity - 0.1 end end end -- displays a button everytime in (x,y) -- object can be a text or a dbitmap -- if user clicks onto it, fn is executed once local Script_buttons = {} local function create_button(x, y, object, fn, extra_options) local always_on_client, always_on_game, ref_x, ref_y, button_pressed if extra_options then always_on_client, always_on_game, ref_x, ref_y, button_pressed = extra_options.always_on_client, extra_options.always_on_game, extra_options.ref_x, extra_options.ref_y, extra_options.button_pressed end local width, height local object_type = type(object) if object_type == "string" then width, height = SNES9X_FONT_WIDTH, SNES9X_FONT_HEIGHT x, y, width = text_position(x, y, object, width, height, always_on_client, always_on_game, ref_x, ref_y) elseif object_type == "boolean" then width, height = SNES9X_FONT_WIDTH, SNES9X_FONT_HEIGHT x, y = text_position(x, y, nil, width, height, always_on_client, always_on_game, ref_x, ref_y) else error"Type of buttton not supported yet" end -- draw the button if button_pressed then draw_rectangle(x, y, width, height, "white", "#d8d8d8ff") -- unlisted colours else draw_rectangle(x, y, width, height, "#606060ff", "#b0b0b0ff") end gui.line(x, y, x + width, y, button_pressed and "#606060ff" or "white") gui.line(x, y, x, y + height, button_pressed and "#606060ff" or "white") if object_type == "string" then gui.text(x + 1, y + 1, object, COLOUR.button_text, 0) elseif object_type == "boolean" then draw_rectangle(x + 1, y + 1, width - 2, height - 2, "#00ff0080", "#00ff00c0") end -- updates the table of buttons table.insert(Script_buttons, {x = x, y = y, width = width, height = height, object = object, action = fn}) end function Options_menu.print_help() print("\n") print(" - - - TIPS - - - ") print("MOUSE:") print("Use the left click to draw blocks and to see the Map16 properties.") print("Use the right click to toogle the hitbox mode of Mario and sprites.") print("\n") print("CHEATS(better turn off while recording a movie):") print("L+R+up: stop gravity for Mario fly / L+R+down to cancel") print("Use the mouse to drag and drop sprites") print("While paused: B+select to get out of the level") print(" X+select to beat the level (main exit)") print(" A+select to get the secret exit (don't use it if there isn't one)") print("\n") print("OTHERS:") print(fmt("Press \"%s\" for more and \"%s\" for less opacity.", OPTIONS.hotkey_increase_opacity, OPTIONS.hotkey_decrease_opacity)) print("It's better to play without the mouse over the game window.") print(" - - - end of tips - - - ") end local poked = false local apply = false function Options_menu.display() if not Options_menu.show_menu then return end -- Pauses emulator and draws the background Text_opacity = 1.0 draw_rectangle(0, 0, Buffer_width, Buffer_height, COLOUR.mainmenu_outline, COLOUR.mainmenu_bg) -- Font stuff local delta_x = SNES9X_FONT_WIDTH local delta_y = SNES9X_FONT_HEIGHT + 4 local x_pos, y_pos = 4, 4 local tmp -- Exit menu button gui.box(0, 0, Buffer_width, delta_y, "#ffffff60", "#ffffff60") -- tab's shadow / unlisted color create_button(Buffer_width, 0, " X ", function() Options_menu.show_menu = false end, {always_on_client = true, always_on_game = true}) -- Tabs create_button(x_pos, y_pos, "Show/hide", function() Options_menu.current_tab = "Show/hide options" end, {button_pressed = Options_menu.current_tab == "Show/hide options"}) x_pos = x_pos + 9*delta_x + 2 create_button(x_pos, y_pos, "Settings", function() Options_menu.current_tab = "Misc options" end, {button_pressed = Options_menu.current_tab == "Misc options"}) x_pos = x_pos + 8*delta_x + 2 create_button(x_pos, y_pos, "Cheats", function() Options_menu.current_tab = "Cheats" end, {button_pressed = Options_menu.current_tab == "Cheats"}) x_pos = x_pos + 6*delta_x + 2 create_button(x_pos, y_pos, "Debug info", function() Options_menu.current_tab = "Debug info" end, {button_pressed = Options_menu.current_tab == "Debug info"}) x_pos = x_pos + 10*delta_x + 2 create_button(x_pos, y_pos, "Misc tables", function() Options_menu.current_tab = "Sprite miscellaneous tables" end, {button_pressed = Options_menu.current_tab == "Sprite miscellaneous tables"}) x_pos = x_pos + 11*delta_x + 2 create_button(x_pos, y_pos, "Memory edit", function() Options_menu.current_tab = "Memory edit" end, {button_pressed = Options_menu.current_tab == "Memory edit"}) x_pos, y_pos = 4, y_pos + delta_y + 4 if Options_menu.current_tab == "Show/hide options" then local x_temp = 0 -- Player tmp_str = "Player:" gui.text(x_pos, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 4 tmp = OPTIONS.display_player_info and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_player_info = not OPTIONS.display_player_info end) tmp_str = "Info" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.display_player_hitbox and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_player_hitbox = not OPTIONS.display_player_hitbox end) tmp_str = "Hitbox" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.display_interaction_points and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_interaction_points = not OPTIONS.display_interaction_points end) tmp_str = "Interaction points" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.display_tongue_hitbox and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_tongue_hitbox = not OPTIONS.display_tongue_hitbox end) tmp_str = "Tongue hitbox" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = 0 + 4*string.len("Player:") + 4 y_pos = y_pos + delta_y tmp = OPTIONS.display_throw_info and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_throw_info = not OPTIONS.display_throw_info end) tmp_str = "Throw info" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.display_blocked_status and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_blocked_status = not OPTIONS.display_blocked_status end) tmp_str = "Blocked status" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 y_pos = y_pos + delta_y + 4 -- Sprites x_temp = 0 tmp_str = "Sprites:" gui.text(x_pos, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 4 tmp = OPTIONS.display_sprite_info and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_sprite_info = not OPTIONS.display_sprite_info end) tmp_str = "Info" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.display_sprite_hitbox and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.display_sprite_hitbox = not OPTIONS.display_sprite_hitbox end) tmp_str = "Hitboxes" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) y_pos = y_pos + delta_y + 4 tmp = OPTIONS.display_debug_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_info = not OPTIONS.display_debug_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Some Debug Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_movie_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_movie_info = not OPTIONS.display_movie_info end) gui.text(x_pos + delta_x + 3, y_pos, "Display Movie Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_misc_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_misc_info = not OPTIONS.display_misc_info end) gui.text(x_pos + delta_x + 3, y_pos, "Display Misc Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_level_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_level_info = not OPTIONS.display_level_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Level Info?") y_pos = y_pos + delta_y --[[tmp = OPTIONS.display_yoshi_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_yoshi_info = not OPTIONS.display_yoshi_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Yoshi Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_extended_sprite_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_extended_sprite_info = not OPTIONS.display_extended_sprite_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Extended Sprite Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_cluster_sprite_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_cluster_sprite_info = not OPTIONS.display_cluster_sprite_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Cluster Sprite Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_minor_extended_sprite_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_minor_extended_sprite_info = not OPTIONS.display_minor_extended_sprite_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Minor Ext. Spr. Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_bounce_sprite_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_bounce_sprite_info = not OPTIONS.display_bounce_sprite_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Bounce Sprite Info?") y_pos = y_pos + delta_y tmp = OPTIONS.display_static_camera_region and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_static_camera_region = not OPTIONS.display_static_camera_region end) gui.text(x_pos + delta_x + 3, y_pos, "Show Static Camera Region?") y_pos = y_pos + delta_y]] elseif Options_menu.current_tab == "Cheats" then tmp = Cheat.allow_cheats and true or " " create_button(x_pos, y_pos, tmp, function() Cheat.allow_cheats = not Cheat.allow_cheats end) gui.text(x_pos + delta_x + 3, y_pos, "Allow Cheats?", COLOUR.warning) y_pos = y_pos + 2*delta_y if Cheat.allow_cheats then local value, widget_pointer -- Powerup value = u8(WRAM.powerup) gui.text(x_pos, y_pos, fmt("Powerup:%3d", value)) create_button(x_pos + 11*SNES9X_FONT_WIDTH + 2, y_pos, "-", function() Cheat.change_address(WRAM.powerup, -1) end) create_button(x_pos + 12*SNES9X_FONT_WIDTH + 4, y_pos, "+", function() Cheat.change_address(WRAM.powerup, 1) end) y_pos = y_pos + delta_y -- Score value = u24(WRAM.mario_score) gui.text(x_pos, y_pos, fmt("Score:%7d0", value)) create_button(x_pos + 14*SNES9X_FONT_WIDTH + 2, y_pos, "-", function() Cheat.change_address(WRAM.mario_score, -1, 3) end) create_button(x_pos + 15*SNES9X_FONT_WIDTH + 4, y_pos, "+", function() Cheat.change_address(WRAM.mario_score, 1, 3) end) y_pos = y_pos + 2 draw_line(x_pos, y_pos + SNES9X_FONT_HEIGHT, x_pos + 100, y_pos + SNES9X_FONT_HEIGHT, 1, COLOUR.weak) -- Snes9x: basic widget hack widget_pointer = floor(100*math.sqrt((value)/1000000)) if mouse_onregion(x_pos, y_pos + SNES9X_FONT_HEIGHT - 2, x_pos + 100, y_pos + SNES9X_FONT_HEIGHT + 2) and User_input.leftclick then value = math.min(999999, 100*(User_input.xmouse - x_pos)^2) w24(WRAM.mario_score, value) end draw_rectangle(x_pos + widget_pointer - 1, y_pos + SNES9X_FONT_HEIGHT - 2, 2, 4, "#ff0000a0", COLOUR.warning) -- unlisted color y_pos = y_pos + delta_y -- Coins value = u8(WRAM.player_coin) gui.text(x_pos, y_pos, fmt("Coins:%3d", value)) create_button(x_pos + 9*SNES9X_FONT_WIDTH + 2, y_pos, "-", function() Cheat.change_address(WRAM.player_coin, -1) end) create_button(x_pos + 10*SNES9X_FONT_WIDTH + 4, y_pos, "+", function() Cheat.change_address(WRAM.player_coin, 1) end) y_pos = y_pos + delta_y end elseif Options_menu.current_tab == "Misc options" then tmp = OPTIONS.draw_tiles_with_click and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.draw_tiles_with_click = not OPTIONS.draw_tiles_with_click end) gui.text(x_pos + delta_x + 3, y_pos, "Draw tiles with left click?") y_pos = y_pos + delta_y create_button(x_pos, y_pos, "Erase Tiles", function() Tiletable = {}; Layer1_tiles = {}; Layer2_tiles = {} end) y_pos = y_pos + delta_y -- Manage opacity / filter y_pos = y_pos + delta_y gui.text(x_pos, y_pos, "Opacity:") y_pos = y_pos + delta_y create_button(x_pos, y_pos, "-", function() if Filter_opacity >= 1 then Filter_opacity = Filter_opacity - 1 end end) create_button(x_pos + delta_x + 2, y_pos, "+", function() if Filter_opacity <= 9 then Filter_opacity = Filter_opacity + 1 end end) gui.text(x_pos + 2*delta_x + 5, y_pos, "Change filter opacity (" .. 10*Filter_opacity .. "%)") y_pos = y_pos + delta_y create_button(x_pos, y_pos, "-", decrease_opacity) create_button(x_pos + delta_x + 2, y_pos, "+", increase_opacity) gui.text(x_pos + 2*delta_x + 5, y_pos, ("Text opacity: (%.0f%%, %.0f%%)"): format(100*Text_max_opacity, 100*Background_max_opacity)) y_pos = y_pos + delta_y gui.text(x_pos, y_pos, ("'%s' and '%s' are hotkeys for this."): format(OPTIONS.hotkey_decrease_opacity, OPTIONS.hotkey_increase_opacity), COLOUR.weak) y_pos = y_pos + delta_y tmp = OPTIONS.display_mouse_coordinates and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_mouse_coordinates = not OPTIONS.display_mouse_coordinates end) gui.text(x_pos + delta_x + 3, y_pos, "Display mouse coordinates?") y_pos = y_pos + delta_y -- Others y_pos = y_pos + delta_y gui.text(x_pos, y_pos, "Help:") y_pos = y_pos + delta_y create_button(x_pos, y_pos, "Show tips in Snes9x: Console", Options_menu.print_help) elseif Options_menu.current_tab == "Debug info" then tmp = OPTIONS.display_debug_info and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_info = not OPTIONS.display_debug_info end) gui.text(x_pos + delta_x + 3, y_pos, "Show Some Debug Info?", COLOUR.warning) y_pos = y_pos + 2*delta_y tmp = OPTIONS.display_debug_player_extra and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_player_extra = not OPTIONS.display_debug_player_extra end) gui.text(x_pos + delta_x + 3, y_pos, "Player extra info") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_sprite_extra and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_sprite_extra = not OPTIONS.display_debug_sprite_extra end) gui.text(x_pos + delta_x + 3, y_pos, "Sprite extra info") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_sprite_tweakers and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_sprite_tweakers = not OPTIONS.display_debug_sprite_tweakers end) gui.text(x_pos + delta_x + 3, y_pos, "Sprite tweakers") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_extended_sprite and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_extended_sprite = not OPTIONS.display_debug_extended_sprite end) gui.text(x_pos + delta_x + 3, y_pos, "Extended sprites") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_cluster_sprite and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_cluster_sprite = not OPTIONS.display_debug_cluster_sprite end) gui.text(x_pos + delta_x + 3, y_pos, "Cluster sprites") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_minor_extended_sprite and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_minor_extended_sprite = not OPTIONS.display_debug_minor end) gui.text(x_pos + delta_x + 3, y_pos, "Minor Ext. sprites") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_bounce_sprite and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_bounce_sprite = not OPTIONS.display_debug_bounce_sprite end) gui.text(x_pos + delta_x + 3, y_pos, "Bounce sprites") y_pos = y_pos + delta_y tmp = OPTIONS.display_debug_controller_data and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_debug_controller_data = not OPTIONS.display_debug_controller_data end) gui.text(x_pos + delta_x + 3, y_pos, "Controller data (might cause desyncs!)", COLOUR.warning) y_pos = y_pos + delta_y local x_temp = 0 -- Tile Map tmp_str = "Tile Map:" gui.text(x_pos, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 4 tmp = OPTIONS.draw_tile_map_grid and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.draw_tile_map_grid = not OPTIONS.draw_tile_map_grid end) tmp_str = "Grid" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.draw_tile_map_type and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.draw_tile_map_type = not OPTIONS.draw_tile_map_type end) tmp_str = "Tile type" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 --[[ tmp = OPTIONS.draw_tile_map_gfx_type and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.draw_tile_map_gfx_type = not OPTIONS.draw_tile_map_gfx_type end) tmp_str = "Graphical type" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 tmp = OPTIONS.draw_tile_map_phy_type and true or " " create_button(x_pos + x_temp, y_pos, tmp, function() OPTIONS.draw_tile_map_phy_type = not OPTIONS.draw_tile_map_phy_type end) tmp_str = "Physical type" gui.text(x_pos + x_temp + delta_x + 3, y_pos, tmp_str) x_temp = x_temp + 4*string.len(tmp_str) + 12 y_pos = y_pos + delta_y]] elseif Options_menu.current_tab == "Sprite miscellaneous tables" then tmp = OPTIONS.display_miscellaneous_sprite_table and true or " " create_button(x_pos, y_pos, tmp, function() OPTIONS.display_miscellaneous_sprite_table = not OPTIONS.display_miscellaneous_sprite_table end) gui.text(x_pos + delta_x + 3, y_pos, "Show Miscellaneous Sprite Table?", COLOUR.warning) y_pos = y_pos + 2*delta_y local opt = OPTIONS.miscellaneous_sprite_table_number for i = 1, 19 do create_button(x_pos, y_pos, opt[i] and true or " ", function() opt[i] = not opt[i] end) gui.text(x_pos + delta_x + 3, y_pos, "Table " .. i) y_pos = y_pos + delta_y if i%10 == 0 then x_pos, y_pos = 4 + 20*SNES9X_FONT_WIDTH, 3*delta_y + 8 end end elseif Options_menu.current_tab == "Memory edit" then tmp = Memory.allow_edit and true or " " create_button(x_pos, y_pos, tmp, function() Memory.allow_edit = not Memory.allow_edit apply = false end) gui.text(x_pos + delta_x + 3, y_pos, "Allow memory edit?", COLOUR.warning) y_pos = y_pos + 2*delta_y if Memory.allow_edit then local address = OPTIONS.address local value = OPTIONS.value local poke = true local freeze = false local size = OPTIONS.size local crescent = false Memory.edit_method = OPTIONS.edit_method local x_temp, y_temp local widget_pointer --- Address --- -- Address digits, from the right to the left local first_digit = read_digit(address, 6, 16) --floor((address-floor(address/16)*16)/1) local second_digit = read_digit(address, 5, 16) --floor((address-floor(address/(16^2))*(16^2))/16) local third_digit = read_digit(address, 4, 16) --floor((address-floor(address/(16^3))*(16^3))/(16^2)) local fourth_digit = read_digit(address, 3, 16) --floor((address-floor(address/(16^4))*(16^4))/(16^3)) local fifth_digit = read_digit(address, 2, 16) --floor((address-floor(address/(16^5))*(16^5))/(16^4)) local address_shown = string.upper(fmt("%x", address)) local width, height = 12, SNES9X_FONT_HEIGHT gui.text(x_pos + 1, y_pos + 13, "Address:", COLOUR.text) -- [ + ] buttons create_button(x_pos + 8*delta_x + 15, y_pos, " + ", function() if OPTIONS.address < 0x7F0000 then OPTIONS.address = OPTIONS.address + 0x10000 end INI.save_options() end) if OPTIONS.address >= 0x7f0000 then gui.text(x_pos + 8*delta_x + 16, y_pos + 1, " + ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 28, y_pos, " + ", function() if OPTIONS.address + 0x1000 < 0x800000 then OPTIONS.address = OPTIONS.address + 0x1000 end INI.save_options() end) if OPTIONS.address + 0x1000 > 0x800000 then gui.text(x_pos + 8*delta_x + 29, y_pos + 1, " + ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 41, y_pos, " + ", function() if OPTIONS.address + 0x100 < 0x800000 then OPTIONS.address = OPTIONS.address + 0x100 end INI.save_options() end) if OPTIONS.address + 0x100 > 0x800000 then gui.text(x_pos + 8*delta_x + 42, y_pos + 1, " + ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 54, y_pos, " + ", function() if OPTIONS.address + 0x10 < 0x800000 then OPTIONS.address = OPTIONS.address + 0x10 end INI.save_options() end) if OPTIONS.address + 0x10 > 0x800000 then gui.text(x_pos + 8*delta_x + 55, y_pos + 1, " + ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 67, y_pos, " + ", function() if OPTIONS.address + 0x1 < 0x800000 then OPTIONS.address = OPTIONS.address + 0x1 end INI.save_options() end) if OPTIONS.address + 0x1 >= 0x800000 then gui.text(x_pos + 8*delta_x + 68, y_pos + 1, " + ", COLOUR.weak2, 0) end -- "disabled" button y_pos = y_pos + delta_y -- Digits boxes draw_rectangle(x_pos + 8*delta_x + 2, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 15, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 28, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 41, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 54, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 67, y_pos, width, height, COLOUR.text, COLOUR.background) -- Digits x_pos = 43 y_pos = y_pos + 1 local colour = COLOUR.memory gui.text(x_pos, y_pos, "7", colour) x_pos = x_pos + 13 gui.text(x_pos, y_pos, string.upper(fmt("%x", fifth_digit)), colour) x_pos = x_pos + 13 gui.text(x_pos, y_pos, string.upper(fmt("%x", fourth_digit)), colour) x_pos = x_pos + 13 gui.text(x_pos, y_pos, string.upper(fmt("%x", third_digit)), colour) x_pos = x_pos + 13 gui.text(x_pos, y_pos, string.upper(fmt("%x", second_digit)), colour) x_pos = x_pos + 13 gui.text(x_pos, y_pos, string.upper(fmt("%x", first_digit)), colour) x_pos = x_pos + 13 y_pos = y_pos + delta_y - 1 -- [ - ] buttons x_pos = 4 create_button(x_pos + 8*delta_x + 15, y_pos, " - ", function() if OPTIONS.address >= 0x7F0000 then OPTIONS.address = OPTIONS.address - 0x10000 end INI.save_options() end) if OPTIONS.address < 0x7F0000 then gui.text(x_pos + 8*delta_x + 16, y_pos + 1, " - ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 28, y_pos, " - ", function() if OPTIONS.address - 0x1000 > 0x7E0000 then OPTIONS.address = OPTIONS.address - 0x1000 end INI.save_options() end) if OPTIONS.address - 0x1000 < 0x7E0000 then gui.text(x_pos + 8*delta_x + 29, y_pos + 1, " - ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 41, y_pos, " - ", function() if OPTIONS.address - 0x100 > 0x7E0000 then OPTIONS.address = OPTIONS.address - 0x100 end INI.save_options() end) if OPTIONS.address - 0x100 < 0x7E0000 then gui.text(x_pos + 8*delta_x + 42, y_pos + 1, " - ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 54, y_pos, " - ", function() if OPTIONS.address - 0x10 > 0x7E0000 then OPTIONS.address = OPTIONS.address - 0x10 end INI.save_options() end) if OPTIONS.address - 0x10 < 0x7E0000 then gui.text(x_pos + 8*delta_x + 55, y_pos + 1, " - ", COLOUR.weak2, 0) end -- "disabled" button create_button(x_pos + 8*delta_x + 67, y_pos, " - ", function() if OPTIONS.address > 0x7E0000 then OPTIONS.address = OPTIONS.address - 0x1 end INI.save_options() end) if OPTIONS.address <= 0x7E0000 then gui.text(x_pos + 8*delta_x + 68, y_pos + 1, " - ", COLOUR.weak2, 0) end -- "disabled" button --- Size --- x_pos = 5 y_pos = 90 gui.text(x_pos, y_pos, "Size:", COLOUR.text) x_temp, y_temp = 0, delta_y create_button(x_pos + x_temp, y_pos + y_temp, " ", function() OPTIONS.size = 1 INI.save_options() end) if size == 1 then draw_rectangle(x_pos + x_temp + 1, y_pos + y_temp + 1, 8*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") end gui.text(x_pos + delta_x + x_temp + 1, y_pos + y_temp + 1, "1 byte", COLOUR.button_text, 0) x_temp = 33 create_button(x_pos + x_temp, y_pos + y_temp, " ", function() OPTIONS.size = 2 INI.save_options() end) if size == 2 then draw_rectangle(x_pos + x_temp + 1, y_pos + y_temp + 1, 9*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") end gui.text(x_pos + delta_x + x_temp + 1, y_pos + y_temp + 1, "2 bytes", COLOUR.button_text, 0) --[[ REMOVE x_temp = 70 create_button(x_pos + x_temp, y_pos + y_temp, " ", function() OPTIONS.size = 58 INI.save_options() end) if size == 58 then draw_rectangle(x_pos + x_temp + 1, y_pos + y_temp + 1, 14*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") end gui.text(x_pos + delta_x + x_temp + 1, y_pos + y_temp + 1, "Sprite table", COLOUR.button_text, 0)]] --- Value --- x_pos = 5 y_pos = 135 y_temp = 38 gui.text(x_pos, y_pos, "Value:", COLOUR.text) gui.text(x_pos + delta_x, y_pos + delta_y, "in hex", COLOUR.text) gui.text(x_pos + delta_x, y_pos + y_temp, "in dec", COLOUR.text) if size == 1 or size == 58 then -- 2 digits -- Correction, so the value loops when you overflow/underflow if OPTIONS.value > 0xFF then OPTIONS.value = OPTIONS.value%256 elseif OPTIONS.value < 0 then OPTIONS.value = OPTIONS.value + 0x100 end -- Display in decimal local value_dec_shown = fmt("%03d", value) draw_rectangle(x_pos + 8*delta_x, y_pos + y_temp - 1, width + 4, height, COLOUR.text, COLOUR.background) gui.text(x_pos + 8*delta_x + 3, y_pos + y_temp, value_dec_shown, colour) -- Value digits, from the right to the left first_digit = string.upper(fmt("%x", floor((value-floor(value/(16^2))*(16^2))/16))) second_digit = string.upper(fmt("%x", floor((value-floor(value/16)*16)/1))) -- [ + ] buttons x_temp = 12 create_button(x_pos + 5*delta_x + x_temp, y_pos, " + ", function() OPTIONS.value = OPTIONS.value + 0x10 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " + ", function() OPTIONS.value = OPTIONS.value + 0x1 INI.save_options() end) x_temp = x_temp + 13 y_pos = y_pos + delta_y -- Digits boxes draw_rectangle(x_pos + 8*delta_x, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 13, y_pos, width, height, COLOUR.text, COLOUR.background) -- Digits x_temp = 21 y_pos = y_pos + 1 local colour = COLOUR.memory gui.text(x_pos + 4*delta_x + x_temp, y_pos, first_digit, colour) x_temp = x_temp + 13 gui.text(x_pos + 4*delta_x + x_temp, y_pos, second_digit, colour) x_temp = x_temp + 13 y_pos = y_pos + delta_y - 1 -- [ - ] buttons x_temp = 12 create_button(x_pos + 5*delta_x + x_temp, y_pos, " - ", function() OPTIONS.value = OPTIONS.value - 0x10 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " - ", function() OPTIONS.value = OPTIONS.value - 0x1 INI.save_options() end) x_temp = x_temp + 13 -- Slider button y_temp = 26 draw_line(x_pos, y_pos + y_temp, x_pos + 128, y_pos + y_temp, 1, COLOUR.weak) -- Snes9x: basic widget hack widget_pointer = math.floor(128*((value)/255)) if mouse_onregion(x_pos, y_pos + y_temp - 2, x_pos + 128, y_pos + y_temp + 2) and User_input.leftclick then OPTIONS.value = math.floor((User_input.xmouse - x_pos)*(255/128)) end draw_rectangle(x_pos + widget_pointer - 1, y_pos + y_temp - 2, 2, 4, "#ff0000a0", COLOUR.warning) -- unlisted color elseif size == 2 then -- 4 digits -- Correction, so the value loops when you overflow/underflow if OPTIONS.value > 0xFFFF then OPTIONS.value = OPTIONS.value - 0x10000 elseif OPTIONS.value < 0 then OPTIONS.value = OPTIONS.value + 0x10000 end -- Display in decimal local value_dec_shown = fmt("%05d", value) draw_rectangle(x_pos + 8*delta_x, y_pos + y_temp - 1, width + 12, height, COLOUR.text, COLOUR.background) gui.text(x_pos + 8*delta_x + 3, y_pos + y_temp, value_dec_shown, colour) -- Value digits, from the right to the left first_digit = string.upper(fmt("%x", floor((value-floor(value/(16^4))*(16^4))/(16^3)))) second_digit = string.upper(fmt("%x", floor((value-floor(value/(16^3))*(16^3))/(16^2)))) third_digit = string.upper(fmt("%x", floor((value-floor(value/(16^2))*(16^2))/16))) fourth_digit = string.upper(fmt("%x", floor((value-floor(value/16)*16)/1))) -- [ + ] buttons x_temp = 12 create_button(x_pos + 5*delta_x + x_temp, y_pos, " + ", function() OPTIONS.value = OPTIONS.value + 0x1000 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " + ", function() OPTIONS.value = OPTIONS.value + 0x100 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " + ", function() OPTIONS.value = OPTIONS.value + 0x10 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " + ", function() OPTIONS.value = OPTIONS.value + 0x1 INI.save_options() end) x_temp = x_temp + 13 y_pos = y_pos + delta_y -- Digits boxes x_temp = 13 draw_rectangle(x_pos + 8*delta_x, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + x_temp, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 2*x_temp, y_pos, width, height, COLOUR.text, COLOUR.background) draw_rectangle(x_pos + 8*delta_x + 3*x_temp, y_pos, width, height, COLOUR.text, COLOUR.background) -- Digits x_temp = 21 y_pos = y_pos + 1 local colour = COLOUR.memory gui.text(x_pos + 4*delta_x + x_temp, y_pos, first_digit, colour) x_temp = x_temp + 13 gui.text(x_pos + 4*delta_x + x_temp, y_pos, second_digit, colour) x_temp = x_temp + 13 gui.text(x_pos + 4*delta_x + x_temp, y_pos, third_digit, colour) x_temp = x_temp + 13 gui.text(x_pos + 4*delta_x + x_temp, y_pos, fourth_digit, colour) x_temp = x_temp + 13 y_pos = y_pos + delta_y - 1 -- [ - ] buttons x_temp = 12 create_button(x_pos + 5*delta_x + x_temp, y_pos, " - ", function() OPTIONS.value = OPTIONS.value - 0x1000 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " - ", function() OPTIONS.value = OPTIONS.value - 0x100 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " - ", function() OPTIONS.value = OPTIONS.value - 0x10 INI.save_options() end) x_temp = x_temp + 13 create_button(x_pos + 5*delta_x + x_temp, y_pos, " - ", function() OPTIONS.value = OPTIONS.value - 0x1 INI.save_options() end) x_temp = x_temp + 13 -- Slider button y_temp = 26 draw_line(x_pos, y_pos + y_temp, x_pos + 128, y_pos + y_temp, 1, COLOUR.weak) -- Snes9x: basic widget hack widget_pointer = floor(128*((value)/(256*256-1))) if mouse_onregion(x_pos, y_pos + y_temp - 2, x_pos + 128, y_pos + y_temp + 2) and User_input.leftclick then OPTIONS.value = floor((User_input.xmouse - x_pos)*((256*256-1)/128)) end draw_rectangle(x_pos + widget_pointer - 1, y_pos + y_temp - 2, 2, 4, "#ff0000a0", COLOUR.warning) -- unlisted color end --- Sprite table --- x_pos = 135 y_pos = 56 x_temp = 13*delta_x + 2 gui.text(x_pos, y_pos, "Sprite table?") -- Yes/No buttons create_button(x_pos + x_temp, y_pos, " ", function() OPTIONS.edit_sprite_table = true INI.save_options() end) if OPTIONS.edit_sprite_table then draw_rectangle(x_pos + x_temp + 1, y_pos + 1, 5*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") freeze = false end gui.text(x_pos + x_temp + 1, y_pos + 1, " Yes ", COLOUR.button_text, 0) x_temp = 18*delta_x + 3 create_button(x_pos + x_temp, y_pos, " ", function() OPTIONS.edit_sprite_table = false INI.save_options() end) if not OPTIONS.edit_sprite_table then draw_rectangle(x_pos + x_temp + 1, y_pos + 1, 4*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") freeze = false end gui.text(x_pos + x_temp + 1, y_pos + 1, " No ", COLOUR.button_text, 0) y_pos = y_pos + delta_y x_temp = 0 if OPTIONS.edit_sprite_table then local opt = OPTIONS.edit_sprite_table_number for i = 1, SMW2.sprite_max do create_button(x_pos + x_temp, y_pos, " ", function() opt[i] = not opt[i] end) if opt[i] then gui.text(x_pos + x_temp + 3, y_pos + 1, fmt("%02d", i - 1), COLOUR.positive) else gui.text(x_pos + x_temp + 3, y_pos + 1, fmt("%02d", i - 1)) end x_temp = x_temp + 13 --y_pos = y_pos + delta_y if x_pos + x_temp > 242 then x_temp, y_pos = 0, y_pos + delta_y - 3 end end end --- Mode --- x_pos = 150 y_pos = 115 x_temp = 25 gui.text(x_pos, y_pos, "Mode:") y_pos = y_pos + delta_y create_button(x_pos, y_pos, " ", function() OPTIONS.edit_method = "Poke" INI.save_options() end) if Memory.edit_method == "Poke" then draw_rectangle(x_pos + 1, y_pos + 1, 6*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") freeze = false end gui.text(x_pos + delta_x + 1, y_pos + 1, "Poke", COLOUR.button_text, 0) create_button(x_pos + x_temp, y_pos, " ", function() OPTIONS.edit_method = "Freeze" INI.save_options() end) if Memory.edit_method == "Freeze" then draw_rectangle(x_pos + x_temp + 1, y_pos + 1, 8*delta_x - 2, delta_y - 6, "#00ff0080", "#00ff00c0") freeze = true end gui.text(x_pos + x_temp + delta_x + 1, y_pos + 1, "Freeze", COLOUR.button_text, 0) --- Action --- x_pos = 150 y_pos = 145 gui.text(x_pos, y_pos, "Action:") y_pos = y_pos + delta_y create_button(x_pos, y_pos, " ", function() poked = false apply = true end) if apply and OPTIONS.edit_method == "Freeze" then gui.text(x_pos + delta_x + 1, y_pos + 1, "APPLY", COLOUR.weak2, 0) else gui.text(x_pos + delta_x + 1, y_pos + 1, "APPLY", COLOUR.memory) end if OPTIONS.edit_method == "Freeze" then create_button(x_pos, y_pos + delta_y, " ", function() apply = false end) if apply and OPTIONS.edit_method == "Freeze" then gui.text(x_pos + delta_x + 3, y_pos + delta_y + 1, "STOP", COLOUR.warning) else gui.text(x_pos + delta_x + 3, y_pos + delta_y + 1, "STOP", COLOUR.weak2, 0) end end --- Result --- x_pos = 195 y_pos = 145 x_temp = 8*delta_x gui.text(x_pos, y_pos, "Result:") gui.text(x_pos, y_pos + 2*delta_y, "in hex", COLOUR.text) gui.text(x_pos, y_pos + 3*delta_y, "in dec", COLOUR.text) gui.text(x_pos, y_pos + delta_y, address_shown, colour) if OPTIONS.edit_sprite_table then gui.text(x_pos + 7*delta_x, y_pos + delta_y, "(table)", colour) end if size == 1 then -- In hex draw_rectangle(x_pos + x_temp, y_pos + 2*delta_y - 1, width, height, COLOUR.text, COLOUR.background) gui.text(x_pos + x_temp + 3, y_pos + 2*delta_y, string.upper(fmt("%02x", u8(address))), colour) -- In decimal draw_rectangle(x_pos + x_temp, y_pos + 3*delta_y - 1, width + 4, height, COLOUR.text, COLOUR.background) gui.text(x_pos + x_temp + 3, y_pos + 3*delta_y, fmt("%03d", u8(address)), colour) elseif size == 2 then -- In hex draw_rectangle(x_pos + x_temp, y_pos + 2*delta_y - 1, width + 8, height, COLOUR.text, COLOUR.background) gui.text(x_pos + x_temp + 3, y_pos + 2*delta_y, string.upper(fmt("%04x", u16(address))), colour) -- In decimal draw_rectangle(x_pos + x_temp, y_pos + 3*delta_y - 1, width + 12, height, COLOUR.text, COLOUR.background) gui.text(x_pos + x_temp + 3, y_pos + 3*delta_y, fmt("%05d", u16(address)), colour) end if apply and poked then alert_text(Buffer_middle_x - 12*delta_x, 190, " Appling address freeze ", COLOUR.warning, COLOUR.warning_bg) end end end return true end -- Gets input of the 1st controller / Might be deprecated someday... local Joypad = {} local function get_joypad() Joypad = joypad.get(1) for button, status in pairs(Joypad) do Joypad[button] = status and 1 or 0 end end -- ############################################################ -- From gocha's local pad_max = 2 local pad_press, pad_down, pad_up, pad_prev, pad_send = {}, {}, {}, {}, {} local pad_presstime = {} for player = 1, pad_max do pad_press[player] = {} pad_presstime[player] = { start=0, select=0, up=0, down=0, left=0, right=0, A=0, B=0, X=0, Y=0, L=0, R=0 } end local dev_press, dev_down, dev_up, dev_prev = input.get(), {}, {}, {} local dev_presstime = { xmouse=0, ymouse=0, leftclick=0, rightclick=0, middleclick=0, shift=0, control=0, alt=0, capslock=0, numlock=0, scrolllock=0, ["0"]=0, ["1"]=0, ["2"]=0, ["3"]=0, ["4"]=0, ["5"]=0, ["6"]=0, ["7"]=0, ["8"]=0, ["9"]=0, A=0, B=0, C=0, D=0, E=0, F=0, G=0, H=0, I=0, J=0, K=0, L=0, M=0, N=0, O=0, P=0, Q=0, R=0, S=0, T=0, U=0, V=0, W=0, X=0, Y=0, Z=0, F1=0, F2=0, F3=0, F4=0, F5=0, F6=0, F7=0, F8=0, F9=0, F10=0, F11=0, F12=0, F13=0, F14=0, F15=0, F16=0, F17=0, F18=0, F19=0, F20=0, F21=0, F22=0, F23=0, F24=0, backspace=0, tab=0, enter=0, pause=0, escape=0, space=0, pageup=0, pagedown=0, ["end"]=0, home=0, insert=0, delete=0, left=0, up=0, right=0, down=0, numpad0=0, numpad1=0, numpad2=0, numpad3=0, numpad4=0, numpad5=0, numpad6=0, numpad7=0, numpad8=0, numpad9=0, ["numpad*"]=0, ["numpad+"]=0, ["numpad-"]=0, ["numpad."]=0, ["numpad/"]=0, tilde=0, plus=0, minus=0, leftbracket=0, rightbracket=0, semicolon=0, quote=0, comma=0, period=0, slash=0, backslash=0 } -- Scan button presses function scanJoypad() for i = 1, pad_max do pad_prev[i] = copytable(pad_press[i]) pad_press[i] = joypad.get(i) pad_send[i] = copytable(pad_press[i]) -- scan keydowns, keyups pad_down[i] = {} pad_up[i] = {} for k in pairs(pad_press[i]) do pad_down[i][k] = (pad_press[i][k] and not pad_prev[i][k]) pad_up[i][k] = (pad_prev[i][k] and not pad_press[i][k]) end -- count press length for k in pairs(pad_press[i]) do if not pad_press[i][k] then pad_presstime[i][k] = 0 else pad_presstime[i][k] = pad_presstime[i][k] + 1 end end end end -- Scan keyboard/mouse input local function scanInputDevs() dev_prev = copytable(dev_press) dev_press = input.get() -- scan keydowns, keyups dev_down = {} dev_up = {} for k in pairs(dev_presstime) do dev_down[k] = (dev_press[k] and not dev_prev[k]) dev_up[k] = (dev_prev[k] and not dev_press[k]) end -- count press length for k in pairs(dev_presstime) do if not dev_press[k] then dev_presstime[k] = 0 else dev_presstime[k] = dev_presstime[k] + 1 end end end -- Send button presses function sendJoypad() for i = 1, pad_max do joypad.set(i, pad_send[i]) end end --############################################################################# -- SMW2 FUNCTIONS: -- Returns the id of Yoshi; if more than one, the lowest sprite slot local function get_yoshi_id() for i = 0, SMW2.sprite_max - 1 do local id = u8(WRAM.sprite_number + i) local status = u8(WRAM.sprite_status + i) if id == 0x35 and status ~= 0 then return i end end return nil end local Real_frame, Previous_real_frame, Effective_frame, Game_mode local Level_index, Room_index, Level_flag, Current_level local Is_paused, Lock_animation_flag, Player_powerup, Player_animation_trigger local Camera_x, Camera_y local function scan_smw2() --[[ Previous_real_frame = Real_frame or u8(WRAM.real_frame) Real_frame = u8(WRAM.real_frame) Effective_frame = u8(WRAM.effective_frame)]] Game_mode = u16(WRAM.game_mode) Is_paused = u8(WRAM.is_paused) == 1 Level_index = u16(WRAM.level_index) --[[Level_flag = u8(WRAM.level_flag_table + Level_index) Lock_animation_flag = u8(WRAM.lock_animation_flag) Room_index = u24(WRAM.room_index) -- In level frequently used info Player_animation_trigger = u8(WRAM.player_animation_trigger) Player_powerup = u8(WRAM.powerup)]] Camera_x = s16(WRAM.camera_x) Camera_y = s16(WRAM.camera_y) end -- Converts the in-game (x, y) to SNES-screen coordinates local function screen_coordinates(x, y, camera_x, camera_y) -- Sane values camera_x = camera_x or Camera_x or u8(WRAM.camera_x) camera_y = camera_y or Camera_y or u8(WRAM.camera_y) local x_screen = (x - camera_x) local y_screen = (y - camera_y) - Y_CAMERA_OFF return x_screen, y_screen end -- Converts Snes9x-screen coordinates to in-game (x, y) local function game_coordinates(x_snes9x, y_snes9x, camera_x, camera_y) -- Sane values camera_x = camera_x or Camera_x or u8(WRAM.camera_x) camera_y = camera_y or Camera_y or u8(WRAM.camera_y) local x_game = x_snes9x + camera_x local y_game = y_snes9x + Y_CAMERA_OFF + camera_y return x_game, y_game end -- Returns the extreme values that Mario needs to have in order to NOT touch a rectangular object local function display_boundaries(x_game, y_game, width, height, camera_x, camera_y) -- Font Text_opacity = 0.8 Bg_opacity = 0.4 -- Coordinates around the rectangle local left = width*floor(x_game/width) local top = height*floor(y_game/height) left, top = screen_coordinates(left, top, camera_x, camera_y) local right = left + width - 1 local bottom = top + height - 1 -- Reads WRAM values of the player local is_ducking = u8(WRAM.is_ducking) local powerup = Player_powerup local is_small = is_ducking ~= 0 or powerup == 0 -- Left local left_text = string.format("%4d.ff", width*floor(x_game/width) - 16) draw_text(AR_x*left, AR_y*(top+bottom)/2, left_text, false, false, 1.0, 0.5) -- Right local right_text = string.format("%d.01", width*floor(x_game/width) + 15) draw_text(AR_x*right + 2, AR_y*(top+bottom)/2, right_text, false, false, 0.0, 0.5) -- Top local value = (Yoshi_riding_flag and y_game - 16) or y_game local top_text = fmt("%d.ff", width*floor(value/width) - 32) draw_text(AR_x*(left+right)/2, AR_y*top, top_text, false, false, 0.5, 1.0) -- Bottom value = y_game + height - 4 --height*floor(y_game/height) - 3 local bottom_text = fmt("%d.ff", value) draw_text(AR_x*(left+right)/2, AR_y*bottom + 1, bottom_text, false, false, 0.5, 0.0) return left, top end local function read_screens() local screens_number = u8(WRAM.screens_number) local vscreen_number = u8(WRAM.vscreen_number) local hscreen_number = u8(WRAM.hscreen_number) - 1 local vscreen_current = s8(WRAM.y + 1) local hscreen_current = s8(WRAM.x + 1) --local level_mode_settings = u8(WRAM.level_mode_settings) --local b1, b2, b3, b4, b5, b6, b7, b8 = bit.multidiv(level_mode_settings, 128, 64, 32, 16, 8, 4, 2) --draw_text(Buffer_middle_x, Buffer_middle_y, {"%x: %x%x%x%x%x%x%x%x", level_mode_settings, b1, b2, b3, b4, b5, b6, b7, b8}, COLOUR.text, COLOUR.background) local level_type if (level_mode_settings ~= 0) and (level_mode_settings == 0x3 or level_mode_settings == 0x4 or level_mode_settings == 0x7 or level_mode_settings == 0x8 or level_mode_settings == 0xa or level_mode_settings == 0xd) then level_type = "Vertical" ; else level_type = "Horizontal" end return level_type, screens_number, hscreen_current, hscreen_number, vscreen_current, vscreen_number end local function get_map16_value(x_game, y_game) local num_x = floor(x_game/16) local num_y = floor(y_game/16) if num_x < 0 or num_y < 0 then return end -- 1st breakpoint local level_type, screens, _, hscreen_number, _, vscreen_number = read_screens() local max_x, max_y if level_type == "Horizontal" then max_x = 16*(hscreen_number + 1) max_y = 27 else max_x = 32 max_y = 16*(vscreen_number + 1) end if num_x > max_x or num_y > max_y then return end -- 2nd breakpoint local num_id, kind if level_type == "Horizontal" then num_id = 16*27*floor(num_x/16) + 16*num_y + num_x%16 kind = (num_id >= 0 and num_id <= 0x35ff) and 256*u8(0x1c800 + num_id) + u8(0xc800 + num_id) else local nx = floor(num_x/16) local ny = floor(num_y/16) local n = 2*ny + nx local num_id = 16*16*n + 16*(num_y%16) + num_x%16 kind = (num_id >= 0 and num_id <= 0x37ff) and 256*u8(0x1c800 + num_id) + u8(0xc800 + num_id) end if kind then return num_x, num_y, kind end end local function draw_layer1_tiles(camera_x, camera_y) local x_origin, y_origin = screen_coordinates(0, 0, camera_x, camera_y) local x_mouse, y_mouse = game_coordinates(User_input.xmouse, User_input.ymouse, camera_x, camera_y) x_mouse = 16*floor(x_mouse/16) y_mouse = 16*floor(y_mouse/16) for number, positions in ipairs(Layer1_tiles) do -- Calculate the Lsnes coordinates local left = positions[1] + x_origin local top = positions[2] + y_origin local right = left + 15 local bottom = top + 15 local x_game, y_game = game_coordinates(left, top, camera_x, camera_y) -- Returns if block is way too outside the screen if left > - Border_left - 32 and top > - Border_top - 32 and -- Snes9x: w/ 2* right < Screen_width + Border_right + 32 and bottom < Screen_height + Border_bottom + 32 then -- Drawings Text_opacity = 1.0 -- Snes9x --[[cal num_x, num_y, kind = get_map16_value(x_game, y_game) if kind then if kind >= 0x111 and kind <= 0x16d or kind == 0x2b then -- default solid blocks, don't know how to include custom blocks draw_rectangle(left + push_direction, top, 8, 15, 0, COLOUR.block_bg) end draw_rectangle(left, top, 15, 15, kind == SMW2.blank_tile_map16 and COLOUR.blank_tile or COLOUR.block, 0) if Layer1_tiles[number][3] then display_boundaries(x_game, y_game, 16, 16, camera_x, camera_y) -- the text around it end -- Draw Map16 id Text_opacity = 1.0 if kind and x_mouse == positions[1] and y_mouse == positions[2] then draw_text(AR_x*(left + 4), AR_y*top - SNES9X_FONT_HEIGHT, fmt("Map16 (%d, %d), %x", num_x, num_y, kind), false, false, 0.5, 1.0) end end]] draw_rectangle(left, top, 15, 15, COLOUR.blank_tile, 0) -- REMOVE end end end local function draw_layer2_tiles() local layer2x = s16(WRAM.layer2_x_nextframe) local layer2y = s16(WRAM.layer2_y_nextframe) for number, positions in ipairs(Layer2_tiles) do draw_rectangle(-layer2x + positions[1], -layer2y + positions[2], 15, 15, COLOUR.layer2_line, COLOUR.layer2_bg) end end local function draw_tilesets(camera_x, camera_y) if Game_mode ~= SMW2.game_mode_level then return end local x_origin, y_origin = screen_coordinates(0, 0, camera_x, camera_y) --############################################## -- Tile grid Text_opacity = 1.0 local width = 256 --u16(WRAM.level_width) or LEVEL[Level_index].width local height = 128 --u16(WRAM.level_height) or LEVEL[Level_index].height local block_x, block_y local x_pos, y_pos local x_screen, y_screen local screen_number, screen_id local block_id local kind_low, kind_high for screen_region_y = 0, 7 do --y_pos = y_origin + 16*screen_region_y for screen_region_x = 0, 15 do --x_pos = x_origin + 16*screen_region_x --x_screen, y_screen = screen_coordinates(x_pos, y_pos, camera_x, camera_y) --x_screen = x_screen + camera_x --y_screen = y_screen + camera_y screen_number = screen_region_y*16 + screen_region_x screen_id = u8(SFXRAM.screen_number_to_id + screen_number) for block_y = 0, 15 do y_pos = y_origin + 256*screen_region_y + 16*block_y for block_x = 0, 15 do x_pos = x_origin + 256*screen_region_x + 16*block_x x_screen, y_screen = screen_coordinates(x_pos, y_pos, camera_x, camera_y) x_screen = x_screen + camera_x y_screen = y_screen + camera_y block_id = 256*screen_id + 16*block_y + block_x if x_screen >= -256 and x_screen <= 256+16 and y_screen >= -256 and y_screen <= 224+16 then -- to not print the whole level, it's too laggy --local num_x, num_y, kind_low, kind_high, address_low, address_high = get_map16_value(x_game, y_game) kind_low = u8(0x7E0000 + WRAM.Map16_data + 2*block_id) -- kind_high = u8(0x7E0000 + WRAM.Map16_data + 2*block_id + 1) -- -- Tile type if OPTIONS.draw_tile_map_type then draw_text(x_pos + 5, y_pos + 1, fmt("%02x\n%02x", kind_high, kind_low), COLOUR.blank_tile) end -- Grid if OPTIONS.draw_tile_map_grid then local block_is_solid = false for i = 1, #SOLID_BLOCKS do if kind_high == SOLID_BLOCKS[i] then block_is_solid = true break end end if block_is_solid then draw_rectangle(x_pos, y_pos, 15, 15, COLOUR.block, 0) else draw_rectangle(x_pos, y_pos, 15, 15, COLOUR.blank_tile, 0) end --[[ if kind_high == 0x01 or kind_high == 0x02 or kind_high == 0x03 or kind_high == 0x05 or kind_high == 0x06 or kind_high == 0x08 or kind_high == 0x0A or kind_high == 0x0C or kind_high == 0x0D or kind_high == 0x0F or kind_high == 0x10 or kind_high == 0x15 or kind_high == 0x1A or kind_high == 0x1B or kind_high == 0x1C or kind_high == 0x29 or kind_high == 0x2C or kind_high == 0x2F or kind_high == 0x38 or kind_high == 0x39 or kind_high == 0x3E or kind_high == 0x3F or kind_high == 0x40 or kind_high == 0x41 or kind_high == 0x44 or kind_high == 0x45 or kind_high == 0x4E or kind_high == 0x50 or kind_high == 0x53 or kind_high == 0x55 or kind_high == 0x57 or kind_high == 0x59 or kind_high == 0x5B or kind_high == 0x5D or kind_high == 0x5F or kind_high == 0x66 or kind_high == 0x67 or kind_high == 0x6B or kind_high == 0x6E or kind_high == 0x79 or kind_high == 0x7D or kind_high == 0x90 or kind_high == 0x9D then draw_rectangle(x_pos, y_pos, 15, 15, COLOUR.block, 0) else draw_rectangle(x_pos, y_pos, 15, 15, COLOUR.blank_tile, 0) end]] --draw_rectangle(x_pos, y_pos, 15, 15, COLOUR.blank_tile, 0) -- REMOVE --draw_text(x_pos, y_pos + 1, fmt("%x", num_id), COLOUR.blank_tile) -- REMOVE if block_y == 0 and block_x == 0 then -- REMOVE --screen_number = floor(num_id/256) + (num_id/16)%256 --screen_id = u8(0x700CAA + screen_number) draw_rectangle(x_pos, y_pos, 22, 16, COLOUR.warning, COLOUR.warning) draw_text(x_pos + 2, y_pos + 1, fmt("#:%02x\nID:%02x", screen_number, screen_id), COLOUR.text) draw_rectangle(x_pos, y_pos, 255, 255, COLOUR.warning, 0) end end --[[ -- Tile type graphicaly if OPTIONS.draw_tile_map_gfx_type then if OPTIONS.draw_tile_map_phy_type then draw_text(x_pos + 5, y_pos + 1, fmt("%02x", kind_low), COLOUR.blank_tile) else draw_text(x_pos + 5, y_pos + 4, fmt("%02x", kind_low), COLOUR.blank_tile) end end -- Tile type physicaly if OPTIONS.draw_tile_map_phy_type then if OPTIONS.draw_tile_map_gfx_type then draw_text(x_pos + 5, y_pos + 8, fmt("%02x", kind_high), COLOUR.blank_tile) else draw_text(x_pos + 5, y_pos + 4, fmt("%02x", kind_high), COLOUR.blank_tile) end end]] end end end end end --############################################## -- Tiles from click local x_mouse, y_mouse = game_coordinates(User_input.xmouse, User_input.ymouse, camera_x, camera_y) x_mouse = 16*floor(x_mouse/16) y_mouse = 16*floor(y_mouse/16) for number, positions in ipairs(Tiletable) do -- Calculate the Snes9x coordinates local left = positions[1] + x_origin local top = positions[2] + y_origin local right = left + 15 local bottom = top + 15 local x_game, y_game = game_coordinates(left, top, camera_x, camera_y) -- Returns if block is way too outside the screen if left > - Border_left - 32 and top > - Border_top - 32 and -- Snes9x: w/ 2* right < Screen_width + Border_right + 32 and bottom < Screen_height + Border_bottom + 32 then --draw_rectangle(left, top, 15, 15, COLOUR.block, 0) -- Math local num_x, num_y, kind_low, kind_high, address_low, address_high = get_map16_value(x_game, y_game) screen_region_x = floor(x_game/256) screen_region_y = floor(y_game/256) screen_number = screen_region_y*16 + screen_region_x screen_id = u8(SFXRAM.screen_number_to_id + screen_number) block_x = (x_game%256)/16 block_y = (y_game%256)/16 block_id = 256*screen_id + 16*block_y + block_x kind_low = u8(0x7E0000 + WRAM.Map16_data + 2*block_id) -- kind_high = u8(0x7E0000 + WRAM.Map16_data + 2*block_id + 1) -- -- Drawings local block_is_solid = false for i = 1, #SOLID_BLOCKS do if kind_high == SOLID_BLOCKS[i] then block_is_solid = true break end end if OPTIONS.draw_tile_map_grid then -- to make it easier to see when grid is activated draw_rectangle(left, top, 15, 15, COLOUR.warning_soft, 0) -- this color fits well elseif block_is_solid then draw_rectangle(left, top, 15, 15, COLOUR.block, 0) else draw_rectangle(left, top, 15, 15, COLOUR.blank_tile, 0) end if Tiletable[number][3] then display_boundaries(x_game, y_game, 16, 16, camera_x, camera_y) -- the text around it end -- Draw Map16 id if x_mouse == positions[1] and y_mouse == positions[2] then Text_opacity = 0.8 local y_pos --if num_y < 2 then y_pos = bottom + 2*SNES9X_FONT_HEIGHT + 1 else y_pos = top - SNES9X_FONT_HEIGHT end draw_text(left + 6, top - 16, fmt("block %03x", block_id), false, true, 0.5, 1.0) draw_text(left + 6, top - 8, fmt("type: %02x %02x", kind_low, kind_high), false, true, 0.5, 1.0) --draw_text(left + 6, y_pos + SNES9X_FONT_HEIGHT, fmt("%04x (%04x)", address_low, address_high), false, true, 0.5, 1.0) -- REMOVE end end end end -- if the user clicks in a tile, it will be be drawn -- if click is onto drawn region, it'll be erased -- there's a max of possible tiles -- layer_table[n] is an array {x, y, [draw info?]} local function select_tile() if not OPTIONS.draw_tiles_with_click then return end if Game_mode ~= SMW2.game_mode_level then return end local x_mouse, y_mouse = game_coordinates(User_input.xmouse, User_input.ymouse, Camera_x, Camera_y) x_mouse = 16*floor(x_mouse/16) y_mouse = 16*floor(y_mouse/16) for number, positions in ipairs(Tiletable) do -- if mouse points a drawn tile, erase it if x_mouse == positions[1] and y_mouse == positions[2] then if Tiletable[number][3] == false then Tiletable[number][3] = true else table.remove(Tiletable, number) end return end end -- otherwise, draw a new tile if #Tiletable == OPTIONS.max_tiles_drawn then table.remove(Tiletable, 1) Tiletable[OPTIONS.max_tiles_drawn] = {x_mouse, y_mouse, false} else table.insert(Tiletable, {x_mouse, y_mouse, false}) end end --[[local function select_tile(x, y, layer_table) if not OPTIONS.draw_tiles_with_click then return end --if Game_mode ~= SMW2.game_mode_level then return end for number, positions in ipairs(layer_table) do -- if mouse points a drawn tile, erase it if x == positions[1] and y == positions[2] then -- Layer 1 if layer_table == Layer1_tiles then if layer_table[number][3] == false then layer_table[number][3] = true else table.remove(layer_table, number) end -- Layer 2 elseif layer_table == Layer2_tiles then table.remove(layer_table, number) end return end end -- otherwise, draw a new tile if #layer_table == OPTIONS.max_tiles_drawn then table.remove(layer_table, 1) layer_table[OPTIONS.max_tiles_drawn] = {x, y, false} else table.insert(layer_table, {x, y, false}) end end]] -- uses the mouse to select an object local function select_object(mouse_x, mouse_y, camera_x, camera_y) -- Font Text_opacity = 1.0 Bg_opacity = 0.5 local x_game, y_game = game_coordinates(mouse_x, mouse_y, camera_x, camera_y) local obj_id -- Checks if the mouse is over Mario local x_player = s16(WRAM.x) local y_player = s16(WRAM.y) if x_player + 0xe >= x_game and x_player + 0x2 <= x_game and y_player + 0x30 >= y_game and y_player + 0x8 <= y_game then obj_id = "Mario" end if not obj_id and OPTIONS.display_sprite_info then for id = 0, SMW2.sprite_max - 1 do local sprite_status = u8(WRAM.sprite_status + id) if sprite_status ~= 0 and Sprites_info[id].x then -- TODO: see why the script gets here without exporting Sprites_info -- Import some values local x_sprite, y_sprite = Sprites_info[id].x, Sprites_info[id].y local x_screen, y_screen = Sprites_info[id].x_screen, Sprites_info[id].y_screen local boxid = Sprites_info[id].boxid local xoff, yoff = Sprites_info[id].xoff, Sprites_info[id].yoff local width, height = Sprites_info[id].width, Sprites_info[id].height if x_sprite + xoff + width >= x_game and x_sprite + xoff <= x_game and y_sprite + yoff + height >= y_game and y_sprite + yoff <= y_game then obj_id = id break end end end end if not obj_id then return end draw_text(User_input.xmouse, User_input.ymouse - 8, obj_id, true, false, 0.5, 1.0) return obj_id, x_game, y_game end -- This function sees if the mouse if over some object, to change its hitbox mode -- The order is: 1) player, 2) sprite. local function right_click() local id = select_object(User_input.xmouse, User_input.ymouse, Camera_x, Camera_y) if tostring(id) == "Yoshi" then if OPTIONS.display_player_hitbox and OPTIONS.display_interaction_points then OPTIONS.display_interaction_points = false OPTIONS.display_player_hitbox = false elseif OPTIONS.display_player_hitbox then OPTIONS.display_interaction_points = true OPTIONS.display_player_hitbox = false elseif OPTIONS.display_interaction_points then OPTIONS.display_player_hitbox = true else OPTIONS.display_player_hitbox = true end end if id then return end local spr_id = tonumber(id) if spr_id and spr_id >= 0 and spr_id <= SMW2.sprite_max - 1 then local number = Sprites_info[spr_id].number if Sprite_hitbox[spr_id][number].sprite and Sprite_hitbox[spr_id][number].block then Sprite_hitbox[spr_id][number].sprite = false Sprite_hitbox[spr_id][number].block = false elseif Sprite_hitbox[spr_id][number].sprite then Sprite_hitbox[spr_id][number].block = true Sprite_hitbox[spr_id][number].sprite = false elseif Sprite_hitbox[spr_id][number].block then Sprite_hitbox[spr_id][number].sprite = true else Sprite_hitbox[spr_id][number].sprite = true end end if id then return end -- Select layer 2 tiles -- TODO --[[local layer2x = s16(WRAM.layer2_x_nextframe) local layer2y = s16(WRAM.layer2_y_nextframe) local x_mouse, y_mouse = User_input.xmouse + layer2x, User_input.ymouse + layer2y select_tile(16*floor(x_mouse/16), 16*floor(y_mouse/16) - Y_CAMERA_OFF, Layer2_tiles)]] end local function show_movie_info() if not OPTIONS.display_movie_info then return end -- Font Text_opacity = 1.0 Bg_opacity = 1.0 local y_text = - Border_top local x_text = 0 local width = SNES9X_FONT_WIDTH local rec_color = (Readonly or not Movie_active) and COLOUR.text or COLOUR.warning local recording_bg = (Readonly or not Movie_active) and COLOUR.background or COLOUR.warning_bg -- Read-only or read-write? local movie_type = (not Movie_active and "No movie ") or (Readonly and "Movie " or "REC ") alert_text(x_text, y_text, movie_type, rec_color, recording_bg) -- Frame count x_text = x_text + width*string.len(movie_type) local movie_info if Readonly then movie_info = string.format("%d/%d", Lastframe_emulated, Framecount) else movie_info = string.format("%d", Lastframe_emulated) end draw_text(x_text, y_text, movie_info) -- Shows the latest frame emulated, not the frame being run now x_text = x_text + width*string.len(movie_info) -- Rerecord count local rr_info = string.format(" %d ", Rerecords) draw_text(x_text, y_text, rr_info, COLOUR.weak) x_text = x_text + width*string.len(rr_info) -- Lag count draw_text(x_text, y_text, Lagcount, COLOUR.warning) local str = frame_time(Lastframe_emulated) -- Shows the latest frame emulated, not the frame being run now alert_text(Buffer_width, Buffer_height, str, COLOUR.text, recording_bg, false, 1.0, 1.0) if Is_lagged then alert_text(Buffer_middle_x - 3*SNES9X_FONT_WIDTH, 2*SNES9X_FONT_HEIGHT, " LAG ", COLOUR.warning, COLOUR.warning_bg) emu.message("Lag detected!") -- Snes9x end end local function show_misc_info() if not OPTIONS.display_misc_info then return end -- Font Text_opacity = 1.0 Bg_opacity = 1.0 local delta_y = SNES9X_FONT_HEIGHT -- Display local game_mode = string.upper(fmt("%04x", Game_mode)) draw_text(Buffer_width + Border_right - 20, -Border_top, fmt("Mode:%s", game_mode), true, false) local camera_str = fmt("Camera (%d, %d)", Camera_x, Camera_y) draw_text(Buffer_middle_x - string.len(camera_str)*SNES9X_FONT_WIDTH/2, 208, camera_str) local RNG = u16(SFXRAM.RNG) RNG = string.upper(fmt("%04x", RNG)) draw_text(180, 0, fmt("RNG:%s", RNG)) local red_coin_counter = u8(WRAM.red_coin_counter) local star_counter = u16(WRAM.star_counter) local flower_counter = u8(WRAM.flower_counter) local coin_counter = u8(WRAM.coin_counter) local star_effective = math.floor(star_counter/10) local temp_str local x_temp = 0 if Game_mode ~= SMW2.game_mode_level then return end temp_str = fmt("Stars:%d/30(%d)", star_effective, star_counter) draw_text(x_temp, delta_y, temp_str, COLOUR.weak, true) x_temp = x_temp + SNES9X_FONT_WIDTH*string.len(temp_str) + 8 temp_str = fmt("Red coins:%d/20", red_coin_counter) draw_text(x_temp, delta_y, temp_str, COLOUR.weak, true) x_temp = x_temp + SNES9X_FONT_WIDTH*string.len(temp_str) + 8 temp_str = fmt("Flowers:%d/5", flower_counter) draw_text(x_temp, delta_y, temp_str, COLOUR.weak, true) x_temp = x_temp + SNES9X_FONT_WIDTH*string.len(temp_str) + 8 temp_str = fmt("Coins:%d", coin_counter) draw_text(x_temp, delta_y, temp_str, COLOUR.weak, true) end -- Display mouse coordinates right above it local function show_mouse_info() if not OPTIONS.display_mouse_coordinates then return end -- Font Text_opacity = 1.0 -- Snes9x Bg_opacity = 1.0 local x = User_input.xmouse local y = User_input.ymouse local x_level = x + Camera_x local y_level = y + Camera_y if User_input.xmouse >= 0 and User_input.xmouse <= 256 and User_input.ymouse >= 0 and User_input.ymouse <= 224 then alert_text(x, y - 8, fmt("(%d, %d)", x, y), COLOUR.text, COLOUR.background, 0.1, 0.5) alert_text(x, y + 8, fmt("(%d, %d)", x_level, y_level), COLOUR.text, COLOUR.background, 0.1, 0.5) end end -- Shows the controller input as the RAM and SNES registers store it local function show_controller_data() if not (OPTIONS.display_debug_info and OPTIONS.display_debug_controller_data) then return end -- Font Text_opacity = 0.9 local height = SNES9X_FONT_HEIGHT local x_pos, y_pos, x, y, _ = 0, 0, 0, SNES9X_FONT_HEIGHT local controller = memory.readword(0x1000000 + 0x4218) -- Snes9x / BUS area x = draw_over_text(x, y, controller, "BYsS^v<>AXLR0123", COLOUR.warning, false, true) _, y = draw_text(x, y, " (Registers)", COLOUR.warning, false, true) x = x_pos x = draw_over_text(x, y, 256*u8(WRAM.ctrl_1_1) + u8(WRAM.ctrl_1_2), "BYsS^v<>AXLR0123", COLOUR.weak) _, y = draw_text(x, y, " (RAM data)", COLOUR.weak, false, true) x = x_pos draw_over_text(x, y, 256*u8(WRAM.firstctrl_1_1) + u8(WRAM.firstctrl_1_2), "BYsS^v<>AXLR0123", 0, "#0xffff", 0) -- Snes9x end local function level_info() if not OPTIONS.display_level_info then return end if Game_mode ~= SMW2.game_mode_level then return end -- Font Text_opacity = 0.2 -- Snes9x Bg_opacity = 1.0 local x_pos = 68 local y_pos = 216 local color = COLOUR.text Text_opacity = 1.0 Bg_opacity = 1.0 local level_index_str = string.upper(fmt("%02x", Level_index)) -- converts the level index to the game level number local world_number = floor(Level_index/12) + 1 local level_number = fmt("%d", Level_index%12 + 1) if level_number == "9" then level_number = "E" end -- Extra levels draw_text(x_pos, y_pos, fmt("Level:%s (%d - %s)", level_index_str, world_number, level_number), color) --[[ -- Number of screens within the level local level_type, screens_number, hscreen_current, hscreen_number, vscreen_current, vscreen_number = read_screens() draw_text(x_pos, y_pos, fmt("%.1sLevel(%.2x)%s", level_type, level_number, sprite_buoyancy), color, true, false) ; draw_text(x_pos, y_pos + SNES9X_FONT_HEIGHT, fmt("Screens(%d):", screens_number), true) draw_text(x_pos, y_pos + 2*SNES9X_FONT_HEIGHT, fmt("(%d/%d, %d/%d)", hscreen_current, hscreen_number, vscreen_current, vscreen_number), true) ;]] end function draw_blocked_status(x_text, y_text, player_blocked_status, x_speed, y_speed) local bitmap_width = 25 -- Snes9x local bitmap_height = 30 -- Snes9x local block_str = "Block:" local str_len = string.len(block_str) local xoffset = x_text + str_len*SNES9X_FONT_WIDTH local yoffset = y_text + 2 local color_line = COLOUR.warning gui.gdoverlay(xoffset + 3, yoffset, IMAGES.player_blocked_status, Background_max_opacity * Bg_opacity * 0.5) -- Snes9x gui.opacity(Text_max_opacity*Text_opacity) -- Snes9x local blocked_status = {} local was_boosted = false -- Bottom right draw_rectangle(xoffset + 19, yoffset + bitmap_height, 8, 2, COLOUR.text) if bit.test(player_blocked_status, 0) then draw_line(xoffset + 20, yoffset + bitmap_height + 1, xoffset + bitmap_width + 1, yoffset + bitmap_height + 1, 1, color_line) end -- Bottom middle draw_rectangle(xoffset + 11, yoffset + bitmap_height, 8, 2, COLOUR.text) if bit.test(player_blocked_status, 1) then draw_line(xoffset + 12, yoffset + bitmap_height + 1, xoffset + 18, yoffset + bitmap_height + 1, 1, color_line) --if x_speed > 0 then was_boosted = true end end -- Bottom left draw_rectangle(xoffset + 3, yoffset + bitmap_height, 8, 2, COLOUR.text) if bit.test(player_blocked_status, 2) then draw_line(xoffset + 4, yoffset + bitmap_height + 1, xoffset + 10, yoffset + bitmap_height + 1, 1, color_line) --if x_speed < 0 then was_boosted = true end end -- Top right draw_rectangle(xoffset + 15, yoffset - 3, 12, 2, COLOUR.text) if bit.test(player_blocked_status, 3) then draw_line(xoffset + 16, yoffset - 2, xoffset + bitmap_width + 1, yoffset - 2, 1, color_line) --if y_speed > 6 then was_boosted = true end end -- Top left draw_rectangle(xoffset + 3, yoffset - 3, 12, 2, COLOUR.text) if bit.test(player_blocked_status, 4) then draw_line(xoffset + 4, yoffset - 2, xoffset + 14, yoffset - 2, 1, color_line) end -- Right body draw_rectangle(xoffset + bitmap_width + 3, yoffset + 14, 2, 15, COLOUR.text) if bit.test(player_blocked_status, 5) then draw_line(xoffset + bitmap_width + 4, yoffset + 15, xoffset + bitmap_width + 4, yoffset + bitmap_height - 2, 1, color_line) end -- Right head draw_rectangle(xoffset + bitmap_width + 3, yoffset, 2, 14, COLOUR.text) if bit.test(player_blocked_status, 6) then draw_line(xoffset + bitmap_width + 4, yoffset + 1, xoffset + bitmap_width + 4, yoffset + 13, 1, color_line) end -- Left body draw_rectangle(xoffset, yoffset + 14, 2, 15, COLOUR.text) if bit.test(player_blocked_status, 7) then draw_line(xoffset + 1, yoffset + 15, xoffset + 1, yoffset + bitmap_height - 2, 1, color_line) end -- Left head draw_rectangle(xoffset, yoffset, 2, 14, COLOUR.text) if bit.test(player_blocked_status, 8) then draw_line(xoffset + 1, yoffset + 1, xoffset + 1, yoffset + 13, 1, color_line) end -- Fully inside the ground if player_blocked_status == 0x1ff then draw_line(xoffset + 3, yoffset + floor(bitmap_height/2) - 1, xoffset + bitmap_width + 2, yoffset + floor(bitmap_height/2) - 1, 1, color_line) draw_line(xoffset + floor(bitmap_width/2) + 3, yoffset, xoffset + floor(bitmap_width/2) + 3, yoffset + bitmap_height - 1, 1, color_line) end draw_text(x_text, y_text, block_str, COLOUR.text, was_boosted and COLOUR.warning_bg or nil) end -- displays player's hitbox local function player_hitbox(x, y, tongue_x_screen, tongue_y_screen) --player_hitbox(x, y, is_ducking, powerup, transparency_level) local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) local yoshi_hitbox = nil --local is_small = is_ducking ~= 0 or powerup == 0 local x_points = X_INTERACTION_POINTS local y_points = Y_INTERACTION_POINTS[2] --[[if is_small and not Yoshi_riding_flag then y_points = Y_INTERACTION_POINTS[1] elseif not is_small and not Yoshi_riding_flag then y_points = Y_INTERACTION_POINTS[2] elseif is_small and Yoshi_riding_flag then y_points = Y_INTERACTION_POINTS[3] else y_points = Y_INTERACTION_POINTS[4] end]] --draw_box(x_screen + x_points.left_side, y_screen + y_points.head, x_screen + x_points.right_side, y_screen + y_points.foot, --COLOUR.interaction_bg, COLOUR.interaction_bg) ; if OPTIONS.display_player_hitbox then -- Collision with sprites local mario_bg = (not Yoshi_riding_flag and COLOUR.mario_bg) or COLOUR.mario_mounted_bg draw_box(x_screen + x_points.left_side - 1, y_screen + y_points.sprite, x_screen + x_points.right_side + 1, y_screen + y_points.foot + 1, COLOUR.mario, mario_bg) ; end -- Interaction points (collision with blocks) if OPTIONS.display_interaction_points then -- Background for block interaction draw_box(x_screen - 1, y_screen + 2, x_screen + 17, y_screen + 33, COLOUR.interaction_nohitbox, COLOUR.interaction_nohitbox_bg) local color = COLOUR.interaction if not SHOW_PLAYER_HITBOX then --draw_box(x_screen + x_points.left_side , y_screen + y_points.head, -- x_screen + x_points.right_side, y_screen + y_points.foot, COLOUR.interaction_nohitbox, COLOUR.interaction_nohitbox_bg) end --- Horizontal lines -- Feet gui.line(x_screen + 3, y_screen + 31, x_screen + 13, y_screen + 31, color) --gui.line(x_screen + 10, y_screen + 31, x_screen + 13, y_screen + 31, color) -- Shoulders gui.line(x_screen + 1, y_screen + 9, x_screen + 3, y_screen + 9, color) gui.line(x_screen + 13, y_screen + 9, x_screen + 15, y_screen + 9, color) -- "Knees" gui.line(x_screen + 2, y_screen + 23, x_screen + 4, y_screen + 23, color) gui.line(x_screen + 12, y_screen + 23, x_screen + 14, y_screen + 23, color) -- Head gui.line(x_screen + 6, y_screen + 4, x_screen + 10, y_screen + 4, color) --- Vertical lines -- Body gui.line(x_screen + 15, y_screen + 9, x_screen + 15, y_screen + 23, color) gui.line(x_screen + 1, y_screen + 9, x_screen + 1, y_screen + 23, color) -- Feet gui.line(x_screen + 3, y_screen + 31, x_screen + 3, y_screen + 29, color) gui.line(x_screen + 8, y_screen + 31, x_screen + 8, y_screen + 29, color) gui.line(x_screen + 13, y_screen + 31, x_screen + 13, y_screen + 29, color) -- Head (top) gui.line(x_screen + 6, y_screen + 4, x_screen + 6, y_screen + 6, color) gui.line(x_screen + 10, y_screen + 4, x_screen + 10, y_screen + 6, color) end --local x_test, y_test = s16(0x700156), s16(0x700158) -- REMOVE --gui.line(x_test, y_test, 100, 100, "blue") -- REMOVE --gui.crosshair(x_test, y_test, 2, "blue") -- REMOVE -- Tongue hitbox if OPTIONS.display_tongue_hitbox then local tongue_state = u8(SFXRAM.tongue_state) local ammo_in_mouth = u8(SFXRAM.ammo_in_mouth) local sprite_in_mouth = false for i = 0, SMW2.sprite_max - 1 do if Sprites_info[i].sprite_status == 0x08 then sprite_in_mouth = true end end if tongue_state ~= 0 and tongue_state < 5 and ammo_in_mouth == 0 and not sprite_in_mouth then draw_box(tongue_x_screen - 14, tongue_y_screen - 10, tongue_x_screen + 13, tongue_y_screen + 9, COLOUR.positive) end end --[[ -- That's the pixel that appears when Mario dies in the pit Show_player_point_position = Show_player_point_position or y_screen >= 200 or (OPTIONS.display_debug_info and OPTIONS.display_debug_player_extra) if Show_player_point_position then draw_rectangle(x_screen - 1, y_screen - 1, 2, 2, COLOUR.interaction_bg, COLOUR.text) Show_player_point_position = false end]] return x_points, y_points end local function egg_throw_info(egg_target_x, egg_target_y, direction, x_centered, y_centered, x_screen, y_screen) if Is_paused then return end --- Memory reading local egg_target_radial_pos = u8(SFXRAM.egg_target_radial_pos) local egg_target_radial_subpos = u8(SFXRAM.egg_target_radial_subpos) local egg_throw_state = u8(SFXRAM.egg_throw_state) local egg_throw_state_timer = u8(SFXRAM.egg_throw_state_timer) --- Transformations local egg_throw_origin_x, egg_throw_origin_y -- found with tests if direction == RIGHT_ARROW then egg_throw_origin_x = x_centered - 2 egg_throw_origin_y = y_centered - 20 else egg_throw_origin_x = x_centered - 14 egg_throw_origin_y = y_centered - 20 end local target_delta_x, target_delta_y = egg_target_x - egg_throw_origin_x, egg_target_y - egg_throw_origin_y local extended_target_x, extended_target_y = egg_target_x + target_delta_x, egg_target_y - target_delta_y local egg_throw_effective_timer -- found with tests and math if egg_throw_state == 10 then egg_throw_effective_timer = 27 elseif egg_throw_state == 9 then egg_throw_effective_timer = 26 elseif egg_throw_state == 8 then egg_throw_effective_timer = 3*egg_throw_state + egg_throw_state_timer - 1 elseif egg_throw_state == 7 then egg_throw_effective_timer = 23 elseif egg_throw_state == 6 then egg_throw_effective_timer = 3*egg_throw_state + egg_throw_state_timer elseif egg_throw_state == 5 then egg_throw_effective_timer = 3*egg_throw_state + egg_throw_state_timer elseif egg_throw_state == 4 then egg_throw_effective_timer = 15 elseif egg_throw_state == 3 then egg_throw_effective_timer = 14 elseif egg_throw_state == 2 then egg_throw_effective_timer = 13 elseif egg_throw_state == 1 then egg_throw_effective_timer = egg_throw_state_timer elseif egg_throw_state == 0 then egg_throw_effective_timer = egg_throw_state_timer -- or egg_throw_state end local egg_target_x_screen, egg_target_y_screen = screen_coordinates(egg_target_x, egg_target_y, Camera_x, Camera_y) local egg_throw_origin_x_screen, egg_throw_origin_y_screen = screen_coordinates(egg_throw_origin_x, egg_throw_origin_y, Camera_x, Camera_y) local radius = floor(sqrt(target_delta_x^2 + target_delta_y^2)) --- Prints -- Target if egg_throw_effective_timer == 18 then draw_arrow(egg_throw_origin_x_screen, egg_throw_origin_y_screen, egg_target_x_screen, egg_target_y_screen, COLOUR.warning_soft) gui.crosshair(egg_target_x_screen, egg_target_y_screen, 2, "blue") draw_text(egg_target_x_screen, egg_target_y_screen + 16, fmt("%02d.%02x", egg_target_radial_pos, egg_target_radial_subpos), COLOUR.positive) end -- Radius if egg_throw_effective_timer == 18 then --draw_text(egg_target_x_screen + 8, egg_target_y_screen, fmt("%d", radius), COLOUR.positive) -- REMOVE if direction == RIGHT_ARROW then for i = -pi/2, 5*pi/18, pi/80 do draw_pixel(egg_throw_origin_x_screen + cos(i)*(68) , egg_throw_origin_y_screen + sin(i)*(68), COLOUR.text) --draw_text(0, 150 + i*10*SNES9X_FONT_HEIGHT, fmt("sin:%f cos:%f", sin(i), cos(i)), COLOUR.text) -- REMOVE end else for i = 13*pi/18, 3*pi/2, pi/80 do draw_pixel(egg_throw_origin_x_screen + cos(i)*(68) , egg_throw_origin_y_screen + sin(i)*(68), COLOUR.text) --draw_text(0, 150 + i*10*SNES9X_FONT_HEIGHT, fmt("sin:%f cos:%f", sin(i), cos(i)), COLOUR.text) -- REMOVE end end end -- Extended target if egg_throw_effective_timer == 18 then draw_arrow(egg_target_x_screen, egg_target_y_screen, egg_target_x_screen + 2*target_delta_x, egg_target_y_screen + 2*target_delta_y, COLOUR.blank_tile) end -- Timer if egg_throw_effective_timer ~= 0 then alert_text(x_screen + 4, y_screen - 16, fmt(" %d ", egg_throw_effective_timer), COLOUR.positive, COLOUR.warning_bg) end end local function player() if not OPTIONS.display_player_info then return end if Game_mode ~= SMW2.game_mode_level then return end if Is_paused then return end -- Font Text_opacity = 1.0 -- Reads SFXRAM and maybe WRAM local x = s16(SFXRAM.x) local y = s16(SFXRAM.y) local x_sub = u8(SFXRAM.x_sub) local y_sub = u8(SFXRAM.y_sub) local x_speed = s8(SFXRAM.x_speed) local x_subspeed = u8(SFXRAM.x_subspeed) local y_speed = s8(SFXRAM.y_speed) local y_subspeed = u8(SFXRAM.y_subspeed) local direction = u8(SFXRAM.direction) local ground_pound_timer = u8(SFXRAM.ground_pound_timer) local ground_pound_state = u8(SFXRAM.ground_pound_state) local player_blocked_status = u16(SFXRAM.player_blocked_status) local on_sprite_platform = u16(SFXRAM.on_sprite_platform) local x_centered = u16(SFXRAM.x_centered) local y_centered = u16(SFXRAM.y_centered) local tongue_x = s16(SFXRAM.tongue_x) local tongue_y = s16(SFXRAM.tongue_y) local egg_target_x = s16(SFXRAM.egg_target_x) local egg_target_y = s16(SFXRAM.egg_target_y) --local diving_status = s8(SFXRAM.diving_status) --local player_item = u8(SFXRAM.player_item) --local is_ducking = u8(SFXRAM.is_ducking) --local on_ground = u8(SFXRAM.on_ground) --local can_jump_from_water = u8(SFXRAM.can_jump_from_water) --local carrying_item = u8(SFXRAM.carrying_item) -- Transformations if direction == 0 then direction = RIGHT_ARROW else direction = LEFT_ARROW end local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) local x_centered_screen, y_centered_screen = screen_coordinates(x_centered, y_centered, Camera_x, Camera_y) local tongue_x_screen, tongue_y_screen = screen_coordinates(tongue_x + x_centered, tongue_y + y_centered, Camera_x, Camera_y) --- Table local i = 0 local delta_x = SNES9X_FONT_WIDTH local delta_y = SNES9X_FONT_HEIGHT local table_x = 0 local table_y = AR_y*22 local temp_colour, x_spd_str, y_spd_str draw_text(table_x, table_y + i*delta_y, fmt("Pos (%+d.%02x, %+d.%02x) %s", x, x_sub, y, y_sub, direction)) i = i + 1 if x_speed < 0 then -- corretions for negative horizontal speed x_speed = x_speed + 1 x_subspeed = 0x100 - x_subspeed if x_subspeed == 0x100 then x_subspeed = 0 ; x_speed = x_speed - 1 end if x_speed == 0 then x_spd_str = fmt("-%d.%02x", x_speed, x_subspeed) -- force negative signal due to previous math else x_spd_str = fmt("%d.%02x", x_speed, x_subspeed) end else x_spd_str = fmt("%+d.%02x", x_speed, x_subspeed) end if y_speed < 0 then -- corretions for negative vertical speed y_speed = y_speed + 1 y_subspeed = 0x100 - y_subspeed if y_subspeed == 0x100 then y_subspeed = 0 ; y_speed = y_speed - 1 end if y_speed == 0 then y_spd_str = fmt("-%d.%02x", y_speed, y_subspeed) -- force negative signal due to previous math else y_spd_str = fmt("%d.%02x", y_speed, y_subspeed) end else y_spd_str = fmt("%+d.%02x", y_speed, y_subspeed) end draw_text(table_x, table_y + i*delta_y, "Speed (" .. x_spd_str .. ", " .. y_spd_str .. ")") i = i + 1 draw_text(table_x, table_y + i*delta_y, fmt("Tongue (%+d, %+d)", tongue_x, tongue_y)) if OPTIONS.display_tongue_hitbox then gui.crosshair(tongue_x_screen, tongue_y_screen, 2, COLOUR.positive) end i = i + 1 draw_text(table_x, table_y + i*delta_y, fmt("Target (%+d, %+d)", egg_target_x, egg_target_y)) i = i + 1 draw_text(table_x, table_y + i*delta_y, fmt("Center (%+d, %+d)", x_centered, y_centered)) -- REMOVE maybe i = i + 1 local can_jump -- block or sprite if bit.test(player_blocked_status, 0) or bit.test(player_blocked_status, 1) or bit.test(player_blocked_status, 2) or on_sprite_platform == 1 then can_jump = "yes" temp_colour = COLOUR.positive else can_jump = "no" temp_colour = COLOUR.warning end draw_text(table_x, table_y + i*delta_y, fmt("Can jump:")) draw_text(table_x + 9*delta_x + 2, table_y + i*delta_y, fmt("%s", can_jump), temp_colour) i = i + 1 if OPTIONS.display_blocked_status then draw_blocked_status(table_x, table_y + i*delta_y + 2, player_blocked_status, x_speed, y_speed) i = i + 1 end --- Other info if ground_pound_timer ~= 0 and ground_pound_timer ~= 255 and ground_pound_state == 0 then alert_text(x_screen + 4, y_screen + 40, fmt(" %d ", ground_pound_timer), COLOUR.positive, COLOUR.warning_bg) end -- shows hitbox, interaction points, and tongue hitbox for player player_hitbox(x, y, tongue_x_screen, tongue_y_screen) --player_hitbox(x, y, is_ducking, powerup, 1.0) -- TODO --[[if OPTIONS.display_static_camera_region then -- TODO Show_player_point_position = true local left_cam, right_cam = u16(0x07142c), u16(0x07142e) -- unlisted WRAM / Snes9x memory bank draw_box(left_cam, 0, right_cam, 224, COLOUR.static_camera_region, COLOUR.static_camera_region) end]] -- Egg throw info if OPTIONS.display_throw_info then egg_throw_info(egg_target_x, egg_target_y, direction, x_centered, y_centered, x_screen, y_screen) end if OPTIONS.display_debug_player_extra then gui.crosshair(x_screen, y_screen, 2, COLOUR.text) gui.crosshair(x_centered_screen, y_centered_screen, 2, COLOUR.text) end end local function extended_sprites() if not OPTIONS.display_extended_sprite_info then return end if Is_paused then return end -- Font Text_opacity = 1.0 local height = SNES9X_FONT_HEIGHT local y_pos = AR_y*144 local counter = 0 for id = 0, SMW2.extended_sprite_max - 1 do local extspr_number = u8(WRAM.extspr_number + id) if extspr_number ~= 0 then -- Reads WRAM addresses local x = 256*u8(WRAM.extspr_x_high + id) + u8(WRAM.extspr_x_low + id) local y = 256*u8(WRAM.extspr_y_high + id) + u8(WRAM.extspr_y_low + id) local sub_x = bit.rshift(u8(WRAM.extspr_subx + id), 4) local sub_y = bit.rshift(u8(WRAM.extspr_suby + id), 4) local x_speed = s8(WRAM.extspr_x_speed + id) local y_speed = s8(WRAM.extspr_y_speed + id) local extspr_table = u8(WRAM.extspr_table + id) local extspr_table2 = u8(WRAM.extspr_table2 + id) -- Reduction of useless info local special_info = "" if OPTIONS.display_debug_info and OPTIONS.display_debug_extended_sprite and (extspr_table ~= 0 or extspr_table2 ~= 0) then special_info = fmt("(%x, %x) ", extspr_table, extspr_table2) end -- x speed for Fireballs if extspr_number == 5 then x_speed = 16*x_speed end if OPTIONS.display_extended_sprite_info then draw_text(Buffer_width + Border_right, y_pos + counter*height, fmt("#%.2d %.2x %s(%d.%x(%+.2d), %d.%x(%+.2d))", id, extspr_number, special_info, x, sub_x, x_speed, y, sub_y, y_speed), COLOUR.extended_sprites, true, false) end if (OPTIONS.display_debug_info and OPTIONS.display_debug_extended_sprite) or not UNINTERESTING_EXTENDED_SPRITES[extspr_number] or (extspr_number == 1 and extspr_table2 == 0xf) then local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) local t = HITBOX_EXTENDED_SPRITE[extspr_number] or {xoff = 0, yoff = 0, width = 16, height = 16, color_line = COLOUR.awkward_hitbox, color_bg = COLOUR.awkward_hitbox_bg} local xoff = t.xoff local yoff = t.yoff + Y_CAMERA_OFF local xrad = t.width local yrad = t.height local color_line = t.color_line or COLOUR.extended_sprites local color_bg = t.color_bg or COLOUR.extended_sprites_bg if extspr_number == 0x5 or extspr_number == 0x11 then color_bg = (Real_frame - id)%4 == 0 and COLOUR.special_extended_sprite_bg or 0 end draw_rectangle(x_screen+xoff, y_screen+yoff, xrad, yrad, color_line, color_bg) -- regular hitbox -- Experimental: attempt to show Mario's fireball vs sprites -- this is likely wrong in some situation, but I can't solve this yet if extspr_number == 5 or extspr_number == 1 then local xoff_spr = x_speed >= 0 and -5 or 1 local yoff_spr = - floor(y_speed/16) - 4 + (y_speed >= -40 and 1 or 0) local yrad_spr = y_speed >= -40 and 19 or 20 draw_rectangle(x_screen + xoff_spr, y_screen + yoff_spr, 12, yrad_spr, color_line, color_bg) end end counter = counter + 1 end end Text_opacity = 0.5 local x_pos, y_pos, length = draw_text(Buffer_width + Border_right, y_pos, fmt("Ext. spr:%2d ", counter), COLOUR.weak, true, false, 0.0, 1.0) if u8(WRAM.spinjump_flag) ~= 0 and u8(WRAM.powerup) == 3 then local fireball_timer = u8(WRAM.spinjump_fireball_timer) draw_text(x_pos - length - SNES9X_FONT_WIDTH, y_pos, fmt("%d %s", fireball_timer%16, bit.test(fireball_timer, 4) and RIGHT_ARROW or LEFT_ARROW), COLOUR.extended_sprites, true, false, 1.0, 1.0) end end local function cluster_sprites() if not OPTIONS.display_cluster_sprite_info or u8(WRAM.cluspr_flag) == 0 then return end -- Font Text_opacity = 1.0 local height = SNES9X_FONT_HEIGHT local x_pos, y_pos = AR_x*90, AR_y*57 -- Snes9x has no space to draw 20 lines local counter = 0 if OPTIONS.display_debug_info and OPTIONS.display_debug_cluster_sprite then draw_text(x_pos, y_pos, "Cluster Spr.", COLOUR.weak) counter = counter + 1 end local reappearing_boo_counter for id = 0, SMW2.cluster_sprite_max - 1 do local clusterspr_number = u8(WRAM.cluspr_number + id) if clusterspr_number ~= 0 then if not HITBOX_CLUSTER_SPRITE[clusterspr_number] then print("Warning: wrong cluster sprite number:", clusterspr_number) -- should not happen without cheats return end -- Reads WRAM addresses local x = signed(256*u8(WRAM.cluspr_x_high + id) + u8(WRAM.cluspr_x_low + id), 16) local y = signed(256*u8(WRAM.cluspr_y_high + id) + u8(WRAM.cluspr_y_low + id), 16) local clusterspr_timer, special_info, table_1, table_2, table_3 -- Reads cluster's table local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) local t = HITBOX_CLUSTER_SPRITE[clusterspr_number] or {xoff = 0, yoff = 0, width = 16, height = 16, color_line = COLOUR.awkward_hitbox, color_bg = COLOUR.awkward_hitbox_bg, oscillation = 1} local xoff = t.xoff local yoff = t.yoff + Y_CAMERA_OFF local xrad = t.width local yrad = t.height local phase = t.phase or 0 local oscillation = (Real_frame - id)%t.oscillation == phase local color = t.color or COLOUR.cluster_sprites local color_bg = t.bg or COLOUR.sprites_bg local invencibility_hitbox = nil if OPTIONS.display_debug_info and OPTIONS.display_debug_cluster_sprite then table_1 = u8(WRAM.cluspr_table_1 + id) table_2 = u8(WRAM.cluspr_table_2 + id) table_3 = u8(WRAM.cluspr_table_3 + id) draw_text(x_pos, y_pos + counter*height, ("#%d(%d): (%d, %d) %d, %d, %d") :format(id, clusterspr_number, x, y, table_1, table_2, table_3), color) counter = counter + 1 end -- Case analysis if clusterspr_number == 3 or clusterspr_number == 8 then clusterspr_timer = u8(WRAM.cluspr_timer + id) if clusterspr_timer ~= 0 then special_info = " " .. clusterspr_timer end elseif clusterspr_number == 6 then table_1 = table_1 or u8(WRAM.cluspr_table_1 + id) if table_1 >= 111 or (table_1 < 31 and table_1 >= 16) then yoff = yoff + 17 elseif table_1 >= 103 or table_1 < 16 then invencibility_hitbox = true elseif table_1 >= 95 or (table_1 < 47 and table_1 >= 31) then yoff = yoff + 16 end elseif clusterspr_number == 7 then reappearing_boo_counter = reappearing_boo_counter or u8(WRAM.reappearing_boo_counter) invencibility_hitbox = (reappearing_boo_counter > 0xde) or (reappearing_boo_counter < 0x3f) special_info = " " .. reappearing_boo_counter end -- Hitbox and sprite id color = invencibility_hitbox and COLOUR.weak or color color_bg = (invencibility_hitbox and 0) or (oscillation and color_bg) or 0 draw_rectangle(x_screen + xoff, y_screen + yoff, xrad, yrad, color, color_bg) draw_text(AR_x*(x_screen + xoff) + xrad, AR_y*(y_screen + yoff), special_info and id .. special_info or id, color, false, false, 0.5, 1.0) end end end local function minor_extended_sprites() if not OPTIONS.display_minor_extended_sprite_info then return end -- Font gui.opacity(1.0) Text_opacity = 1.0 local height = SNES9X_FONT_HEIGHT local x_pos, y_pos = 0, Buffer_height - height*SMW2.minor_extended_sprite_max local counter = 0 for id = 0, SMW2.minor_extended_sprite_max - 1 do local minorspr_number = u8(WRAM.minorspr_number + id) if minorspr_number ~= 0 then -- Reads WRAM addresses local x = signed(256*u8(WRAM.minorspr_x_high + id) + u8(WRAM.minorspr_x_low + id), 16) local y = signed(256*u8(WRAM.minorspr_y_high + id) + u8(WRAM.minorspr_y_low + id), 16) local xspeed, yspeed = s8(WRAM.minorspr_xspeed + id), s8(WRAM.minorspr_yspeed + id) local x_sub, y_sub = u8(WRAM.minorspr_x_sub + id), u8(WRAM.minorspr_y_sub + id) local timer = u8(WRAM.minorspr_timer + id) -- Only sprites 1 and 10 use the higher byte local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) if minorspr_number ~= 1 and minorspr_number ~= 10 then -- Boo stream and Piece of brick block x_screen = x_screen%0x100 y_screen = y_screen%0x100 end -- Draw next to the sprite local text = "#" .. id .. (timer ~= 0 and (" " .. timer) or "") draw_text(AR_x*(x_screen + 8), AR_y*(y_screen + 4), text, COLOUR.minor_extended_sprites, false, false, 0.5, 1.0) if minorspr_number == 10 then -- Boo stream draw_rectangle(x_screen + 4, y_screen + 4 + Y_CAMERA_OFF, 8, 8, COLOUR.minor_extended_sprites, COLOUR.sprites_bg) end -- Draw in the table if OPTIONS.display_debug_info and OPTIONS.display_debug_minor_extended_sprite then draw_text(x_pos, y_pos + counter*height, ("#%d(%d): %d.%x(%d), %d.%x(%d)") :format(id, minorspr_number, x, floor(x_sub/16), xspeed, y, floor(y_sub/16), yspeed), COLOUR.minor_extended_sprites) end counter = counter + 1 end end if OPTIONS.display_debug_info and OPTIONS.display_debug_minor_extended_sprite then draw_text(x_pos, y_pos - height, "Minor Ext Spr:" .. counter, COLOUR.weak) end end local function bounce_sprite_info() if not OPTIONS.display_bounce_sprite_info then return end -- Debug info local x_txt, y_txt = AR_x*90, AR_y*37 if OPTIONS.display_debug_info and OPTIONS.display_debug_bounce_sprite then Text_opacity = 0.5 draw_text(x_txt, y_txt, "Bounce Spr.", COLOUR.weak) end -- Font Text_opacity = 0.6 local height = SNES9X_FONT_HEIGHT local stop_id = (u8(WRAM.bouncespr_last_id) - 1)%SMW2.bounce_sprite_max for id = 0, SMW2.bounce_sprite_max - 1 do local bounce_sprite_number = u8(WRAM.bouncespr_number + id) if bounce_sprite_number ~= 0 then local x = 256*u8(WRAM.bouncespr_x_high + id) + u8(WRAM.bouncespr_x_low + id) local y = 256*u8(WRAM.bouncespr_y_high + id) + u8(WRAM.bouncespr_y_low + id) local bounce_timer = u8(WRAM.bouncespr_timer + id) if OPTIONS.display_debug_info and OPTIONS.display_debug_bounce_sprite then draw_text(x_txt, y_txt + height*(id + 1), fmt("#%d:%d (%d, %d)", id, bounce_sprite_number, x, y)) end local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) x_screen, y_screen = AR_x*(x_screen + 8), AR_y*y_screen local color = id == stop_id and COLOUR.warning or COLOUR.text draw_text(x_screen , y_screen, fmt("#%d:%d", id, bounce_timer), color, false, false, 0.5) -- timer -- Turn blocks if bounce_sprite_number == 7 then turn_block_timer = u8(WRAM.turn_block_timer + id) draw_text(x_screen, y_screen + height, turn_block_timer, color, false, false, 0.5) end end end end local function sprite_info(id, counter, table_position) Text_opacity = 1.0 -- id to read memory correctly local id_off = 4*id local sprite_status = u8(SFXRAM.sprite_status + id_off) if sprite_status == 0 then return 0 end -- returns if the slot is empty local sprite_type = u16(SFXRAM.sprite_type + id_off) local x = s16(SFXRAM.sprite_x + id_off + 2) local y = s16(SFXRAM.sprite_y + id_off + 2) local x_speed = s8(SFXRAM.sprite_x_speed + id_off) local x_subspeed = u8(SFXRAM.sprite_x_subspeed + id_off) local y_speed = s8(SFXRAM.sprite_y_speed + id_off) local y_subspeed = u8(SFXRAM.sprite_y_subspeed + id_off) local x_centered = u16(SFXRAM.sprite_x_center + id_off) local y_centered = u16(SFXRAM.sprite_y_center + id_off) --local x_sub = u8(WRAM.sprite_x_sub + id_off) --local y_sub = u8(WRAM.sprite_y_sub + id_off) --local stun = u8(WRAM.sprite_miscellaneous7 + id_off) --local underwater = u8(WRAM.sprite_underwater + id_off) --local x_offscreen = s8(WRAM.sprite_x_offscreen + id_off) --local y_offscreen = s8(WRAM.sprite_y_offscreen + id_off) sprite_type = string.upper(fmt("%03x", sprite_type)) -- to make it easier to read local special = "" --[[if (OPTIONS.display_debug_info and OPTIONS.display_debug_sprite_extra) or ((sprite_status ~= 0x8 and sprite_status ~= 0x9 and sprite_status ~= 0xa and sprite_status ~= 0xb) or stun ~= 0) then special = string.format("(%d %d) ", sprite_status, stun) end]] -- TODO ---********************************************** -- Calculates the sprites dimensions and screen positions local x_screen, y_screen = screen_coordinates(x, y, Camera_x, Camera_y) local x_centered_screen, y_centered_screen = screen_coordinates(x_centered, y_centered, Camera_x, Camera_y) if Game_mode == 0x0007 then -- adjustment for the intro "glitched" sprite positions x_screen = x_screen + Camera_x if Camera_y ~= 0 then y_screen = y_screen + Camera_y end --elseif Game_mode == 0x002C then y_screen = y_screen - 256 end -- Sprite clipping vs mario and sprites local boxid = bit.band(u8(WRAM.sprite_2_tweaker + id_off), 0x3f) -- This is the type of box of the sprite local xoff = HITBOX_SPRITE[boxid].xoff local yoff = HITBOX_SPRITE[boxid].yoff + Y_CAMERA_OFF local sprite_half_width = u16(SFXRAM.sprite_hitbox_half_width + id_off) --HITBOX_SPRITE[boxid].width local sprite_half_height = u16(SFXRAM.sprite_hitbox_half_height + id_off) --HITBOX_SPRITE[boxid].height -- Sprite clipping vs objects local clip_obj = bit.band(u8(WRAM.sprite_1_tweaker + id_off), 0xf) -- type of hitbox for blocks local xpt_right = OBJ_CLIPPING_SPRITE[clip_obj].xright local ypt_right = OBJ_CLIPPING_SPRITE[clip_obj].yright local xpt_left = OBJ_CLIPPING_SPRITE[clip_obj].xleft local ypt_left = OBJ_CLIPPING_SPRITE[clip_obj].yleft local xpt_down = OBJ_CLIPPING_SPRITE[clip_obj].xdown local ypt_down = OBJ_CLIPPING_SPRITE[clip_obj].ydown local xpt_up = OBJ_CLIPPING_SPRITE[clip_obj].xup local ypt_up = OBJ_CLIPPING_SPRITE[clip_obj].yup -- Process interaction with player every frame? -- Format: dpmksPiS. This 'm' bit seems odd, since it has false negatives local oscillation_flag = bit.test(u8(WRAM.sprite_4_tweaker + id_off), 5) or OSCILLATION_SPRITES[number] -- calculates the correct color to use, according to id local info_color = COLOUR.sprites[id%(#COLOUR.sprites) + 1] local color_background = COLOUR.sprites_bg --[[ sprite_type == 0x35 then -- TODO info_color = COLOUR.yoshi color_background = COLOUR.yoshi_bg else info_color = COLOUR.sprites[id%(#COLOUR.sprites) + 1] color_background = COLOUR.sprites_bg end]] --[[if (not oscillation_flag) and (Real_frame - id)%2 == 1 then color_background = 0 end -- due to sprite oscillation every other frame -- notice that some sprites interact with Mario every frame ;]] -- TODO ---********************************************** -- Displays sprites hitboxes -- TODO if OPTIONS.display_sprite_hitbox then gui.crosshair(x_centered_screen, y_centered_screen, 2, COLOUR.text) draw_box(x_centered_screen - sprite_half_width, y_centered_screen - sprite_half_height, x_centered_screen + sprite_half_width, y_centered_screen + sprite_half_height, info_color) --[[ -- That's the pixel that appears when the sprite vanishes in the pit if y_screen >= 224 or (OPTIONS.display_debug_info and OPTIONS.display_debug_sprite_extra) then draw_pixel(x_screen, y_screen, info_color) end if Sprite_hitbox[id][sprite_type].block then draw_box(x_screen + xpt_left, y_screen + ypt_down, x_screen + xpt_right, y_screen + ypt_up, COLOUR.sprites_clipping_bg, Sprite_hitbox[id][sprite_type].sprite and 0 or COLOUR.sprites_clipping_bg) end if Sprite_hitbox[id][sprite_type].sprite and not ABNORMAL_HITBOX_SPRITES[sprite_type] then -- show sprite/sprite clipping draw_rectangle(x_screen + xoff, y_screen + yoff, sprite_width, sprite_height, info_color, color_background) end if Sprite_hitbox[id][sprite_type].block then -- show sprite/object clipping local size, color = 1, COLOUR.sprites_interaction_pts draw_line(x_screen + xpt_right, y_screen + ypt_right, x_screen + xpt_right - size, y_screen + ypt_right, 1, color) -- right draw_line(x_screen + xpt_left, y_screen + ypt_left, x_screen + xpt_left + size, y_screen + ypt_left, 1, color) -- left draw_line(x_screen + xpt_down, y_screen + ypt_down, x_screen + xpt_down, y_screen + ypt_down - size, 1, color) -- down draw_line(x_screen + xpt_up, y_screen + ypt_up, x_screen + xpt_up, y_screen + ypt_up + size, 1, color) -- up end]] end ---********************************************** -- Special sprites analysis: -- Toadies if sprite_type == "091" then for id = 0, 3 do local toadies_x = s16(WRAM.toadies_relative_x + 4*id) + x local toadies_y = s16(WRAM.toadies_relative_y + 4*id) + y -- Position local toadies_x_screen, toadies_y_screen = screen_coordinates(toadies_x, toadies_y, Camera_x, Camera_y) gui.crosshair(toadies_x_screen, toadies_y_screen, 2, "blue") -- Table draw_text(Buffer_width + Border_right, 176, "Toadies:", "red", true) local toadies_str = fmt("{%02d} %d, %d", id, toadies_x, toadies_y) draw_text(Buffer_width + Border_right, 184 + id*SNES9X_FONT_HEIGHT, toadies_str, "red", true) end end ---********************************************** -- Prints those informations next to the sprite Text_opacity = 0.7 -- Snes9x Bg_opacity = 1.0 if x_screen >= 0 and x_screen <= 256 and y_screen >= 0 and y_screen <= 224 then Text_opacity = 0.8 else Text_opacity = 0.5 end draw_text(x_screen + 8, y_screen - 5, fmt("<%02d>", id), info_color, COLOUR.background, COLOUR.halo, true) -- Sprite position pixel and cross draw_pixel(x_screen, y_screen, info_color, COLOUR.background) if OPTIONS.display_debug_sprite_extra then gui.crosshair(x_screen, y_screen, 2, info_color) end ---********************************************** -- The sprite table: --[[cal x_speed_water = "" -- TODO if underwater ~= 0 then -- if sprite is underwater local correction = floor(3*floor(x_speed/2)/2) x_speed_water = string.format("%+.2d=%+.2d", correction - x_speed, correction) end]] --local sprite_str = fmt("#%02d %02x %s%d.%1x(%+.2d%s) %d.%1x(%+.2d)", --id, sprite_type, special, x, floor(x_sub/16), x_speed, x_speed_water, y, floor(y_sub/16), y_speed) --special = string.upper(fmt("[%02x] ", sprite_status)) -- REMOVE local sprite_str = fmt("<%02d> %s %s%d(%+d.%02x), %d(%+d.%02x)", id, sprite_type, special, x, x_speed, x_subspeed, y, y_speed, y_subspeed) --Text_opacity = 1.0 Bg_opacity = 1.0 if x_screen >= 0 and x_screen <= 256 and y_screen >= 0 and y_screen <= 224 then Text_opacity = 0.8 else Text_opacity = 0.5 end draw_text(Buffer_width + Border_right, table_position + counter*SNES9X_FONT_HEIGHT, sprite_str, info_color, true) -- Miscellaneous sprite table if OPTIONS.display_miscellaneous_sprite_table then -- Font Font = false local x_mis, y_mis = 0, AR_y*144 + counter*SNES9X_FONT_HEIGHT local t = OPTIONS.miscellaneous_sprite_table_number local misc, text = nil, fmt("#%.2d", id) for num = 1, 19 do misc = t[num] and u8(WRAM["sprite_miscellaneous" .. num] + id_off) or false text = misc and fmt("%s %3d", text, misc) or text end draw_text(x_mis, y_mis, text, info_color) end ---********************************************** -- Exporting some values Sprites_info[id].sprite_type = sprite_type Sprites_info[id].sprite_status = sprite_status Sprites_info[id].x, Sprites_info[id].y = x, y Sprites_info[id].x_screen, Sprites_info[id].y_screen = x_screen, y_screen Sprites_info[id].boxid = boxid Sprites_info[id].xoff, Sprites_info[id].yoff = xoff, yoff Sprites_info[id].width, Sprites_info[id].height = sprite_width, sprite_height return 1 end local function sprites() if not OPTIONS.display_sprite_info then return end if Is_paused then return end local valid_game_mode = false if Game_mode == SMW2.game_mode_level then valid_game_mode = true elseif Game_mode == 0x0007 then valid_game_mode = true end -- Intro (0x0007) too if valid_game_mode == false then return end local counter = 0 local table_position = AR_y*22 for id = 0, SMW2.sprite_max - 1 do counter = counter + sprite_info(id, counter, table_position) end -- Font Text_opacity = 0.6 -- Snes9x draw_text(Buffer_width + Border_right, table_position - SNES9X_FONT_HEIGHT, fmt("spr:%.2d", counter), COLOUR.weak, true) -- -- Miscellaneous sprite table: index if OPTIONS.display_miscellaneous_sprite_table then Font = false local t = OPTIONS.miscellaneous_sprite_table_number local text = "Tab" for num = 1, 19 do text = t[num] and fmt("%s %3d", text, num) or text end draw_text(0, AR_y*144 - SNES9X_FONT_HEIGHT, text, info_color) end end local function yoshi() if not OPTIONS.display_yoshi_info then return end -- Font Text_opacity = 1.0 Bg_opacity = 1.0 local x_text = 0 local y_text = AR_y*88 local yoshi_id = Yoshi_id if yoshi_id ~= nil then local eat_id = u8(WRAM.sprite_miscellaneous16 + yoshi_id) local eat_type = u8(WRAM.sprite_number + eat_id) local tongue_len = u8(WRAM.sprite_miscellaneous4 + yoshi_id) local tongue_timer = u8(WRAM.sprite_miscellaneous9 + yoshi_id) local tongue_wait = u8(WRAM.sprite_tongue_wait) local tongue_height = u8(WRAM.yoshi_tile_pos) local tongue_out = u8(WRAM.sprite_miscellaneous13 + yoshi_id) local eat_type_str = eat_id == SMW2.null_sprite_id and "-" or string.format("%02x", eat_type) local eat_id_str = eat_id == SMW2.null_sprite_id and "-" or string.format("#%02d", eat_id) -- Yoshi's direction and turn around local turn_around = u8(WRAM.sprite_miscellaneous14 + yoshi_id) local yoshi_direction = u8(WRAM.sprite_miscellaneous12 + yoshi_id) local direction_symbol if yoshi_direction == 0 then direction_symbol = RIGHT_ARROW else direction_symbol = LEFT_ARROW end draw_text(x_text, y_text, fmt("Yoshi %s %d", direction_symbol, turn_around), COLOUR.yoshi) local h = SNES9X_FONT_HEIGHT if eat_id == SMW2.null_sprite_id and tongue_len == 0 and tongue_timer == 0 and tongue_wait == 0 then Text_opacity = 0.2 -- Snes9x end draw_text(x_text, y_text + h, fmt("(%0s, %0s) %02d, %d, %d", eat_id_str, eat_type_str, tongue_len, tongue_wait, tongue_timer), COLOUR.yoshi) ; -- more WRAM values local yoshi_x = 256*u8(WRAM.sprite_x_high + yoshi_id) + u8(WRAM.sprite_x_low + yoshi_id) local yoshi_y = 256*u8(WRAM.sprite_y_high + yoshi_id) + u8(WRAM.sprite_y_low + yoshi_id) local x_screen, y_screen = screen_coordinates(yoshi_x, yoshi_y, Camera_x, Camera_y) -- invisibility timer local mount_invisibility = u8(WRAM.sprite_miscellaneous18 + yoshi_id) if mount_invisibility ~= 0 then Text_opacity = 0.5 -- Snes9x draw_text(AR_x*(x_screen + 4), AR_y*(y_screen - 12), mount_invisibility, COLOUR.yoshi) end -- Tongue hitbox and timer if tongue_wait ~= 0 or tongue_out ~=0 or tongue_height == 0x89 then -- if tongue is out or appearing -- the position of the hitbox pixel local tongue_direction = yoshi_direction == 0 and 1 or -1 local tongue_high = tongue_height ~= 0x89 local x_tongue = x_screen + 24 - 40*yoshi_direction + tongue_len*tongue_direction x_tongue = not tongue_high and x_tongue or x_tongue - 5*tongue_direction local y_tongue = y_screen + 10 + 11*(tongue_high and 0 or 1) -- the drawing local tongue_line if tongue_wait <= 9 then -- hitbox point vs berry tile draw_rectangle(x_tongue - 1, y_tongue - 1, 2, 2, COLOUR.tongue_bg, COLOUR.text) tongue_line = COLOUR.tongue_line else tongue_line = COLOUR.tongue_bg end -- tongue out: time predictor local tinfo, tcolor if tongue_wait > 9 then tinfo = tongue_wait - 9; tcolor = COLOUR.tongue_line -- not ready yet elseif tongue_out == 1 then tinfo = 17 + tongue_wait; tcolor = COLOUR.text -- tongue going out elseif tongue_out == 2 then -- at the max or tongue going back tinfo = math.max(tongue_wait, tongue_timer) + floor((tongue_len + 7)/4) - (tongue_len ~= 0 and 1 or 0) tcolor = eat_id == SMW2.null_sprite_id and COLOUR.text or COLOUR.warning elseif tongue_out == 0 then tinfo = 0; tcolor = COLOUR.text -- tongue in else tinfo = tongue_timer + 1; tcolor = COLOUR.tongue_line -- item was just spat out end Text_opacity = 0.5 -- Snes9x draw_text(AR_x*(x_tongue + 4), AR_y*(y_tongue + 5), tinfo, tcolor, false, false, 0.5) Text_opacity = 1.0 draw_rectangle(x_tongue, y_tongue + 1, 8, 4, tongue_line, COLOUR.tongue_bg) end end end local function show_counters() if not OPTIONS.display_counters then return end -- Font Text_opacity = 1.0 Bg_opacity = 1.0 local height = SNES9X_FONT_HEIGHT local text_counter = 0 local invincibility_timer = u16(SFXRAM.invincibility_timer) local eat_timer = u16(SFXRAM.eat_timer) local transform_timer = u16(SFXRAM.transform_timer) local star_timer = u16(SFXRAM.star_timer) local switch_timer = u16(WRAM.switch_timer) --local end_level_timer = u8(SFXRAM.end_level_timer) local display_counter = function(label, value, default, mult, frame, color) if value == default then return end text_counter = text_counter + 1 local color = color or COLOUR.text draw_text(0, AR_y*102 + (text_counter * height), fmt("%s: %d", label, (value * mult) - frame), color) end if Player_animation_trigger == 5 or Player_animation_trigger == 6 then display_counter("Pipe", pipe_entrance_timer, -1, 1, 0, COLOUR.counter_pipe) end display_counter("Invincibility", invincibility_timer, 0, 1, 0, COLOUR.counter_pballoon) display_counter("Swallow", eat_timer, 0, 1, 0, COLOUR.counter_multicoin) display_counter("Transform", transform_timer, 0, 1, 0, COLOUR.counter_gray_pow) display_counter("Star", star_timer, 0, 1, 0, COLOUR.counter_blue_pow) if Game_mode == SMW2.game_mode_level then display_counter("Switch", switch_timer, 0, 1, 0, COLOUR.counter_dircoin) end --display_counter("End Level", end_level_timer, 0, 2, (Real_frame - 1) % 2) --if Lock_animation_flag ~= 0 then display_counter("Animation", animation_timer, 0, 1, 0) end -- shows when player is getting hurt or dying end -- Main function to run inside a level local function level_mode() --if Game_mode == SMW2.game_mode_level then -- Draws/Erases the tiles if user clicked draw_tilesets(Camera_x, Camera_y) --draw_layer1_tiles(Camera_x, Camera_y) --draw_layer2_tiles() sprites() --extended_sprites() --cluster_sprites() --minor_extended_sprites() --bounce_sprite_info() level_info() player() --yoshi() show_counters() -- Draws/Erases the hitbox for objects if true or User_input.mouse_inwindow == 1 then select_object(User_input.xmouse, User_input.ymouse, Camera_x, Camera_y) end --end end local function overworld_mode() -- TODO --[[if Game_mode ~= SMW2.game_mode_overworld then return end -- Font Text_opacity = 1.0 Bg_opacity = 1.0 local height = SNES9X_FONT_HEIGHT local y_text = SNES9X_FONT_HEIGHT -- Real frame modulo 8 local real_frame_8 = Real_frame%8 draw_text(Buffer_width + Border_right, y_text, fmt("Real Frame = %3d = %d(mod 8)", Real_frame, real_frame_8), true) -- Star Road info local star_speed = u8(WRAM.star_road_speed) local star_timer = u8(WRAM.star_road_timer) y_text = y_text + height draw_text(Buffer_width + Border_right, y_text, fmt("Star Road(%x %x)", star_speed, star_timer), COLOUR.cape, true)]] end local function left_click() for _, field in pairs(Script_buttons) do -- if mouse is over the button if mouse_onregion(field.x, field.y, field.x + field.width, field.y + field.height) then field.action() return end end -- Drag and drop sprites if Cheat.allow_cheats then local id = select_object(User_input.xmouse, User_input.ymouse, Camera_x, Camera_y) if type(id) == "number" and id >= 0 and id < SMW2.sprite_max then Cheat.dragging_sprite_id = id Cheat.is_dragging_sprite = true return end end if not Options_menu.show_menu then select_tile() end -- Layer 1 tiles --[[local x_mouse, y_mouse = game_coordinates(User_input.xmouse, User_input.ymouse, Camera_x, Camera_y) x_mouse = 16*floor(x_mouse/16) y_mouse = 16*floor(y_mouse/16) if not Options_menu.show_menu then select_tile(x_mouse, y_mouse, Layer1_tiles) end]] end -- This function runs at the end of paint callback -- Specific for info that changes if the emulator is paused and idle callback is called local function snes9x_buttons() -- Font Text_opacity = 1.0 if not Options_menu.show_menu and User_input.mouse_inwindow == 1 then create_button(100, -Border_top, " Menu ", function() Options_menu.show_menu = true end) -- Snes9x create_button(-Border_left, Buffer_height - Border_bottom, Cheat.allow_cheats and "Cheats: allowed" or "Cheats: blocked", function() Cheat.allow_cheats = not Cheat.allow_cheats end, {always_on_client = true, ref_y = 1.0}) ; create_button(Buffer_width + Border_right, Buffer_height + Border_bottom, "Erase Tiles", function() Tiletable = {} end, {always_on_client = true, ref_y = 1.0}) ; else if Cheat.allow_cheats then -- show cheat status anyway Text_opacity = 0.8 draw_text(-Border_left, Buffer_height + Border_bottom, "Cheats: allowed", COLOUR.warning, true, false, 0, 1) end end -- Drag and drop sprites with the mouse if Cheat.is_dragging_sprite then Cheat.drag_sprite(Cheat.dragging_sprite_id) Cheat.is_cheating = true end Options_menu.display() end --############################################################################# -- CHEATS -- This signals that some cheat is activated, or was some short time ago Cheat.allow_cheats = false Cheat.is_cheating = false function Cheat.is_cheat_active() if Cheat.is_cheating then alert_text(Buffer_middle_x - 3*SNES9X_FONT_WIDTH, 0, " Cheat ", COLOUR.warning,COLOUR.warning_bg) Previous.is_cheating = true else if Previous.is_cheating then emu.message("Script applied cheat") Previous.is_cheating = false end end end -- Called from Cheat.beat_level() function Cheat.activate_next_level(secret_exit) if u8(WRAM.level_exit_type) == 0x80 and u8(WRAM.midway_point) == 1 then if secret_exit then w8(WRAM.level_exit_type, 0x2) else w8(WRAM.level_exit_type, 1) end end Cheat.is_cheating = true end -- allows start + select + X to activate the normal exit -- start + select + A to activate the secret exit -- start + select + B to exit the level without activating any exits function Cheat.beat_level() if Is_paused and Joypad["select"] and (Joypad["X"] or Joypad["A"] or Joypad["B"]) then w8(WRAM.level_flag_table + Level_index, bit.bor(Level_flag, 0x80)) local secret_exit = Joypad["A"] if not Joypad["B"] then w8(WRAM.midway_point, 1) else w8(WRAM.midway_point, 0) end Cheat.activate_next_level(secret_exit) end end -- This function makes Mario's position free -- Press L+R+up to activate and L+R+down to turn it off. -- While active, press directionals to fly free and Y or X to boost him up Cheat.under_free_move = false function Cheat.free_movement() if (Joypad["L"] and Joypad["R"] and Joypad["up"]) then Cheat.under_free_move = true end if (Joypad["L"] and Joypad["R"] and Joypad["down"]) then Cheat.under_free_move = false end if not Cheat.under_free_move then if Previous.under_free_move then return end -- w8(SFXRAM.is_frozen, 0) end return end local x_pos, y_pos = u16(SFXRAM.x), u16(SFXRAM.y) --local movement_mode = u8(WRAM.player_animation_trigger) local pixels = (Joypad["Y"] and 7) or (Joypad["X"] and 4) or 1 -- how many pixels per frame -- Math if Joypad["left"] then x_pos = x_pos - pixels end if Joypad["right"] then x_pos = x_pos + pixels end if Joypad["up"] then y_pos = y_pos - pixels end if Joypad["down"] then y_pos = y_pos + pixels end -- Disable normal button behavior if Joypad["down"] then pad_send[1].down = false end -- avoid ground pound --if Joypad["down"] then gui.text(100, 100, "down", COLOUR.text) end if Joypad["A"] then pad_send[1].A = false end -- avoid throwing egg --if Joypad["A"] then gui.text(100, 100, "A", COLOUR.text) end if Joypad["B"] then pad_send[1].B = false end -- avoid "jumping" --if Joypad["B"] then gui.text(100, 100, "B", COLOUR.text) end if Joypad["Y"] then pad_send[1].Y = false end -- avoid licking --if Joypad["Y"] then gui.text(100, 100, "Y", COLOUR.text) end joypad.set(1, pad_send[1]) -- set --[[ freeze player to avoid deaths if movement_mode == 0 then w8(WRAM.frozen, 1) w8(WRAM.x_speed, 0) w8(WRAM.y_speed, 0) -- animate sprites by incrementing the effective frame w8(WRAM.effective_frame, (u8(WRAM.effective_frame) + 1) % 256) else w8(WRAM.frozen, 0) end]] -- manipulate some values w16(SFXRAM.x, x_pos) w16(SFXRAM.y, y_pos) w8(SFXRAM.x_speed, 0) w8(SFXRAM.x_subspeed, 0) w8(SFXRAM.y_speed, -1) w8(SFXRAM.y_subspeed, 96) w8(SFXRAM.invincibility_timer, 120) --w8(WRAM.invincibility_timer, 127) --[[w8(WRAM.vertical_scroll_flag_header, 1) -- free vertical scrolling w8(WRAM.vertical_scroll_enabled, 1)]] Cheat.is_cheating = true Previous.under_free_move = true end -- Drag and drop sprites with the mouse, if the cheats are activated and mouse is over the sprite -- Right clicking and holding: drags the sprite -- Releasing: drops it over the latest spot function Cheat.drag_sprite(id) --if Game_mode ~= SMW2.game_mode_level then Cheat.is_dragging_sprite = false ; return end local xoff, yoff = Sprites_info[id].xoff, Sprites_info[id].yoff local xgame, ygame = game_coordinates(User_input.xmouse - xoff, User_input.ymouse - yoff, Camera_x, Camera_y) local sprite_xhigh = floor(xgame/256) local sprite_xlow = xgame - 256*sprite_xhigh local sprite_yhigh = floor(ygame/256) local sprite_ylow = ygame - 256*sprite_yhigh w8(WRAM.sprite_x_high + id, sprite_xhigh) w8(WRAM.sprite_x_low + id, sprite_xlow) w8(WRAM.sprite_y_high + id, sprite_yhigh) w8(WRAM.sprite_y_low + id, sprite_ylow) end -- Snes9x: modifies address
value from to -- [size] is the optional size in bytes of the address -- TODO: [is_signed] is untrue if the value is unsigned, true otherwise function Cheat.change_address(address, modification, size) size = size or 1 local memoryf_read = (size == 1 and u8) or (size == 2 and u16) or (size == 3 and u24) or error"size is too big" local memoryf_write = (size == 1 and w8) or (size == 2 and w16) or (size == 3 and w24) or error"size is too big" local max_value = 256^size - 1 local current = memoryf_read(address) --if is_signed then max_value = signed(max_value, 8*size) end local new = (current + modification)%(max_value + 1) memoryf_write(address, new) Cheat.is_cheating = true end --############################################################################# -- MEMORY EDIT -- function Memory.edit() if not Memory.allow_edit then return end if not apply then return end if OPTIONS.edit_method == "Freeze" then poked = false else apply = false end local slot = OPTIONS.edit_sprite_table_number if not poked then if OPTIONS.edit_sprite_table then for i = 1, SMW2.sprite_max do if slot[i] then if OPTIONS.size == 2 then memory.writeword(OPTIONS.address + 2*(i - 1), OPTIONS.value) else memory.writebyte(OPTIONS.address + 2*(i - 1), OPTIONS.value) end if OPTIONS.edit_method == "Poke" then emu.message("\n Applied address poke") else alert_text(Buffer_middle_x - 12*SNES9X_FONT_WIDTH, 190, " Appling address freeze ", COLOUR.warning, COLOUR.warning_bg) end poked = true end end else if OPTIONS.size == 2 then memory.writeword(OPTIONS.address, OPTIONS.value) else memory.writebyte(OPTIONS.address, OPTIONS.value) end if OPTIONS.edit_method == "Poke" then emu.message("\n Applied address poke") else alert_text(Buffer_middle_x - 12*SNES9X_FONT_WIDTH, 190, " Appling address freeze ", COLOUR.warning, COLOUR.warning_bg) end poked = true end end end --############################################################################# -- MAIN -- -- Key presses: Keys.registerkeypress("rightclick", right_click) Keys.registerkeypress("leftclick", left_click) Keys.registerkeypress(OPTIONS.hotkey_increase_opacity, increase_opacity) Keys.registerkeypress(OPTIONS.hotkey_decrease_opacity, decrease_opacity) -- Key releases: Keys.registerkeyrelease("mouse_inwindow", function() Cheat.is_dragging_sprite = false end) Keys.registerkeyrelease("leftclick", function() Cheat.is_dragging_sprite = false end) gui.register(function() -- Initial values, don't make drawings here snes9x_status() Script_buttons = {} -- reset the buttons -- Dark filter to cover the game area if Filter_opacity ~= 0 then gui.opacity(Filter_opacity/10) gui.box(0, 0, Buffer_width, Buffer_height, Filter_color) gui.opacity(1.0) end -- Drawings are allowed now scan_smw2() level_mode() overworld_mode() show_movie_info() show_misc_info() show_controller_data() Cheat.is_cheat_active() snes9x_buttons() show_mouse_info() --memory.writebyte(0x7E03B4, 255) -- if Options_menu.current_tab == "Memory edit" then -- REMOVE gui.text(180, 192, string.format("poked = %s", tostring(poked)), COLOUR.warning) gui.text(180, 200, string.format("apply = %s", tostring(apply)), COLOUR.warning) gui.text(170, 208, string.format(".edit_method = %s", tostring(OPTIONS.edit_method)), COLOUR.warning) end end) emu.registerbefore(function() Joypad = joypad.get(1) --get_joypad() scanInputDevs() scanJoypad() sendJoypad() if Cheat.allow_cheats then Cheat.is_cheating = false Cheat.free_movement() Cheat.beat_level() else -- Cancel any continuous cheat Cheat.under_free_move = false Cheat.is_cheating = false end Memory.edit() end) emu.registerexit(function() INI.save_options() print("Finishing Yoshi's Island script.") end) print("Lua script loaded successfully.") while true do -- User input data Previous.User_input = copytable(User_input) local tmp = input.get() for entry, value in pairs(User_input) do User_input[entry] = tmp[entry] or false end User_input.mouse_inwindow = mouse_onregion(0, 0, Buffer_width - 1, Buffer_height - 1) and 1 or 0 -- Snes9x, custom field -- Detect if a key was just pressed or released for entry, value in pairs(User_input) do if (value ~= false) and (Previous.User_input[entry] == false) then Keys.pressed[entry] = true else Keys.pressed[entry] = false end if (value == false) and (Previous.User_input[entry] ~= false) then Keys.released[entry] = true else Keys.released[entry] = false end end -- Key presses/releases execution: for entry, value in pairs(Keys.press) do if Keys.pressed[entry] then value() end end for entry, value in pairs(Keys.release) do if Keys.released[entry] then value() end end -- Lag-flag is accounted correctly only inside this loop Is_lagged = emu.lagged() emu.frameadvance() end