├── weapon_stay_big.lua ├── README.md ├── LICENSE └── monster_has_hp_bar.lua /weapon_stay_big.lua: -------------------------------------------------------------------------------- 1 | log.info("[weapon_stay_big.lua] loaded") 2 | 3 | local typedef = sdk.find_type_definition("snow.player.PlayerWeaponCtrl") 4 | local start_method = typedef:get_method("start") 5 | 6 | sdk.hook(start_method, function(args) 7 | local weapctrl = sdk.to_managed_object(args[2]) 8 | weapctrl:set_field("_bodyConstScale", 1.0) 9 | end, function(retval) end) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monster Hunter: Rise Scripts 2 | This is my collection of REFramework scripts I've made for Monster Hunter: Rise. I'm one of the developers who worked on REFramework's scripting system so these scripts are mostly to showcase what the system is capable of. 3 | 4 | All scripts in this repo are MIT licensed. 5 | 6 | ## Scripts 7 | * `weapon_stay_big.lua` - Stops weapons from shrinking when sheathed or on your back. [Nexus Mods Link](https://www.nexusmods.com/monsterhunterrise/mods/39) 8 | * `monster_has_hp_bar.lua` - Adds a health bar to the top of your screen for the nearest large monster. [Nexus Mods Link](https://www.nexusmods.com/monsterhunterrise/mods/43) 9 | 10 | ## Installation 11 | Most scripts can be installed by just dropping them into your REFramework `/autorun` directory. 12 | 13 | `monster_has_hp_bar.lua` also requires my [REFramework-D2D](https://github.com/cursey/reframework-d2d) plugin. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 cursey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /monster_has_hp_bar.lua: -------------------------------------------------------------------------------- 1 | log.info("[monster_has_hp_bar.lua] loaded") 2 | 3 | local cfg = json.load_file("monster_has_hp_bar.json") 4 | 5 | if not cfg then 6 | cfg = { 7 | font_size = imgui.get_default_font_size() - 2, 8 | font_name = "Tahoma", 9 | is_top_bar = true, 10 | } 11 | end 12 | 13 | re.on_config_save( 14 | function() 15 | json.dump_file("monster_has_hp_bar.json", cfg) 16 | end 17 | ) 18 | 19 | local status = "" 20 | local hp_table = {} 21 | 22 | local physical_param_field = sdk.find_type_definition("snow.enemy.EnemyCharacterBase"):get_field("k__BackingField") 23 | local get_vital_method = physical_param_field:get_type():get_method("getVital") 24 | local vital_param_type = get_vital_method:get_return_type() 25 | local get_current_hp_method = vital_param_type:get_method("get_Current") 26 | local get_max_hp_method = vital_param_type:get_method("get_Max") 27 | local enemy_type_field = sdk.find_type_definition("snow.enemy.EnemyCharacterBase"):get_field("k__BackingField") 28 | local message_manager_type = sdk.find_type_definition("snow.gui.MessageManager") 29 | local get_enemy_message_method = message_manager_type:get_method("getEnemyNameMessage") 30 | local em_boss_character_base_type = sdk.find_type_definition("snow.enemy.EmBossCharacterBase") 31 | 32 | 33 | local msgman = nil 34 | local tick_count = 0 35 | local last_update_tick = 0 36 | local recorded_monsters = {} 37 | local updated_monsters = {} 38 | local num_known_monsters = 0 39 | local num_updated_monsters = 0 40 | local tick_start = 0 41 | 42 | -- run every tick to keep track of monsters 43 | -- whenever we've updated enough monsters to surpass how many we've seen, 44 | -- we reset and start over 45 | -- this allows us to only update one monster per tick to save on performance 46 | re.on_pre_application_entry("UpdateBehavior", function() 47 | tick_count = tick_count + 1 48 | 49 | if num_known_monsters ~= 0 and 50 | num_updated_monsters >= num_known_monsters or 51 | tick_count >= num_known_monsters * 2 52 | then 53 | recorded_monsters = {} 54 | updated_monsters = {} 55 | last_update_tick = 0 56 | tick_count = 0 57 | num_known_monsters = 0 58 | num_updated_monsters = 0 59 | end 60 | end) 61 | 62 | function record_hp(args) 63 | local enemy = sdk.to_managed_object(args[2]) 64 | if not enemy then return end 65 | 66 | if not recorded_monsters[enemy] then 67 | num_known_monsters = num_known_monsters + 1 68 | recorded_monsters[enemy] = true 69 | end 70 | 71 | -- only updates one monster per tick to increase performance 72 | if updated_monsters[enemy] or tick_count == last_update_tick then 73 | return 74 | end 75 | 76 | last_update_tick = tick_count 77 | num_updated_monsters = num_updated_monsters + 1 78 | updated_monsters[enemy] = true 79 | 80 | local physparam = physical_param_field:get_data(enemy) 81 | if not physparam then 82 | status = "No physical param" 83 | return 84 | end 85 | 86 | local vitalparam = get_vital_method:call(physparam, 0, 0) 87 | if not vitalparam then 88 | status = "No vital param" 89 | return 90 | end 91 | 92 | local hp = get_current_hp_method:call(vitalparam) 93 | local max_hp = get_max_hp_method:call(vitalparam) 94 | local hp_entry = hp_table[enemy] 95 | 96 | --[[local vitals = physparam:get_field("_VitalList") 97 | if not vitals then 98 | status = "No vital list" 99 | return 100 | end 101 | 102 | local vital_items = vitals:get_field("mItems") 103 | if not vital_items then 104 | status = "No vital items" 105 | return 106 | end 107 | 108 | local num_vitals = #vital_items:get_elements()]] 109 | 110 | if not hp_entry then 111 | -- Grab enemy name. 112 | if not msgman then 113 | status = "No message manager" 114 | return 115 | end 116 | 117 | local enemy_type = enemy_type_field:get_data(enemy) 118 | if not enemy_type then 119 | status = "No enemy type" 120 | return 121 | end 122 | 123 | hp_entry = {} 124 | hp_table[enemy] = hp_entry 125 | 126 | local name = get_enemy_message_method:call(msgman, enemy_type) 127 | hp_entry.name = name 128 | hp_entry.parts = {} 129 | end 130 | 131 | hp_entry.hp = hp 132 | hp_entry.max_hp = max_hp 133 | end 134 | 135 | local typedef = sdk.find_type_definition("snow.enemy.EnemyCharacterBase") 136 | local update_method = typedef:get_method("update") 137 | 138 | sdk.hook(update_method, 139 | record_hp, 140 | function(retval) end 141 | ) 142 | 143 | re.on_draw_ui( 144 | function() 145 | if not imgui.collapsing_header("Monster Has HP Bar") then return end 146 | 147 | local changed, value = imgui.input_text("Font Name", cfg.font_name) 148 | if changed then cfg.font_name = value end 149 | 150 | changed, value = imgui.slider_int("Font Size", cfg.font_size, 1, 100) 151 | if changed then cfg.font_size = value end 152 | 153 | changed, value = imgui.checkbox("Top Bar", cfg.is_top_bar) 154 | if changed then cfg.is_top_bar = value end 155 | 156 | if imgui.button("Save settings") then 157 | json.dump_file("monster_has_hp_bar.json", cfg) 158 | end 159 | 160 | if string.len(status) > 0 then 161 | imgui.text("Status: " .. status) 162 | end 163 | end 164 | ) 165 | 166 | local font = nil 167 | 168 | d2d.register( 169 | function() 170 | font = d2d.create_font(cfg.font_name, cfg.font_size, true) 171 | end, 172 | function() 173 | msgman = sdk.get_managed_singleton("snow.gui.MessageManager") 174 | 175 | if not msgman then 176 | status = "No message manager" 177 | return 178 | end 179 | 180 | local playman = sdk.get_managed_singleton("snow.player.PlayerManager") 181 | if not playman then 182 | status = "No player manager" 183 | return 184 | end 185 | 186 | local me = playman:call("findMasterPlayer") 187 | if not me then 188 | status = "No local player" 189 | return 190 | end 191 | 192 | local gameobj = me:call("get_GameObject") 193 | if not gameobj then 194 | status = "No local player game object" 195 | return 196 | end 197 | 198 | local transform = gameobj:call("get_Transform") 199 | if not transform then 200 | status = "No local player transform" 201 | return 202 | end 203 | 204 | local me_pos = transform:call("get_Position") 205 | if not me_pos then 206 | status = "No local player position" 207 | return 208 | end 209 | 210 | local enemyman = sdk.get_managed_singleton("snow.enemy.EnemyManager") 211 | if not enemyman then 212 | status = "No enemy manager" 213 | return 214 | end 215 | 216 | local closest_enemy = nil 217 | local closest_dist = 999999 218 | 219 | for i = 0, 4 do 220 | local enemy = enemyman:call("getBossEnemy", i) 221 | if not enemy then 222 | break 223 | end 224 | 225 | local hp_entry = hp_table[enemy] 226 | if not hp_entry then 227 | status = "No hp entry" 228 | break 229 | end 230 | 231 | local gameobj = enemy:call("get_GameObject") 232 | if not gameobj then 233 | status = "No game object" 234 | break 235 | end 236 | 237 | local transform = gameobj:call("get_Transform") 238 | if not transform then 239 | status = "No transform" 240 | break 241 | end 242 | 243 | local enemy_pos = transform:call("get_Position") 244 | if not enemy_pos then 245 | status = "No position" 246 | break 247 | end 248 | 249 | local distance = (me_pos - enemy_pos):length() 250 | 251 | if distance < closest_dist then 252 | closest_dist = distance 253 | closest_enemy = enemy 254 | end 255 | end 256 | 257 | if not closest_enemy then 258 | hp_table = {} 259 | status = "No enemy" 260 | return 261 | end 262 | 263 | local hp_entry = hp_table[closest_enemy] 264 | if not hp_entry then 265 | status = "No hp entry" 266 | return 267 | end 268 | 269 | local screen_w, screen_h = d2d.surface_size() 270 | local x = 0 271 | local y = 0 272 | local w = screen_w 273 | local h = 20 274 | local hp_percent = hp_entry.hp / hp_entry.max_hp 275 | local hp_w = w * hp_percent 276 | local missing_hp_w = w - hp_w 277 | 278 | local str = hp_entry.name .. " " .. math.floor(hp_percent * 100) .. "% " .. math.floor(hp_entry.hp) .. "/" .. math.floor(hp_entry.max_hp) 279 | local text_w, text_h = d2d.measure_text(font, str) 280 | h = text_h 281 | 282 | if not cfg.is_top_bar then 283 | y = screen_h - h 284 | end 285 | 286 | d2d.fill_rect(x + hp_w, y, missing_hp_w, h, 0xAA000000) 287 | d2d.fill_rect(x, y, hp_w, h, 0xAA228B22) 288 | d2d.text(font, str, x, y, 0xFFFFFFFF) 289 | 290 | status = "" 291 | end 292 | ) 293 | --------------------------------------------------------------------------------