From 14927795cde224012dcca52e0b67bd993fb023b7 Mon Sep 17 00:00:00 2001 From: Gamaun007 Date: Wed, 8 Jul 2026 17:20:39 +0200 Subject: [PATCH 1/3] Per-magazine attachment coordinates (backward compatible) Each offset field (positions/rotations/scale/parent_bone) can now be overridden per magazine section via keys suffixed with the mag section: s_magazine___ = ... Unsuffixed shared keys keep working exactly as before and remain the fallback, so existing weapon configs are unaffected. The suffixed key is resolved against the loaded mag; during the mid-reload swap the loaded mag is still nil/old, so it falls back to the incoming/ pending section the core already captures (incoming_mag_section / pending_swap_mag_section) - the model lands on its final coords immediately instead of jumping when the data commits. The swap path (change_specific_mag_model) applies the per-mag scale for the same reason. Documented in 2_guide_on_adding_to_new_anim_sets.ltx. Co-Authored-By: Claude Opus 4.8 --- .../2_guide_on_adding_to_new_anim_sets.ltx | 26 +++++++++++- gamedata/scripts/scripted_magazines.script | 40 +++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/gamedata/configs/2_guide_on_adding_to_new_anim_sets.ltx b/gamedata/configs/2_guide_on_adding_to_new_anim_sets.ltx index bf5e11a..5838048 100644 --- a/gamedata/configs/2_guide_on_adding_to_new_anim_sets.ltx +++ b/gamedata/configs/2_guide_on_adding_to_new_anim_sets.ltx @@ -22,4 +22,28 @@ And thats it! it should be working once you relaunch the game ADDING NEW MAGAZINE MODELs: preferably, you should try to align the mag models and their bones so they all line up Then add the sections and models associated to the "scripted_magazines.script"'s "mag_models" table -Thats it ! But remember to actually make them appear you need to do the above \ No newline at end of file +Thats it ! But remember to actually make them appear you need to do the above +PER-MAG COORDINATES (optional, backward compatible): +By default every mag on a gun uses the same shared offset (s_magazine_positions_[BONE], etc). +If a particular mag model has a different pivot / size and needs its own offset, add a key +suffixed with that magazine's SECTION name: + s_magazine_positions_[BONE]_[MAG_SECTION] = x,y,z + s_magazine_rotations_[BONE]_[MAG_SECTION] = x,y,z + s_magazine_scale_[BONE]_[MAG_SECTION] = float + s_magazine_parent_bone_[BONE]_[MAG_SECTION] = [bone] +[MAG_SECTION] is the loaded magazine item section, e.g. mag_ak_5.45x39_extended. +Resolution per field: if the suffixed key exists it is used, otherwise the script falls back +to the shared s_magazine_..._[BONE] key. So guns that only define the shared keys keep working +exactly as before - the shared coords are applied to every mag on that gun. + +Example (bone "magazin", standard mag uses shared coords, the longer 45-round mag overrides): + ![wpn_ak74] + s_magazine_hide_bones = magazin + s_magazine_parent_bone_magazin = magazin + ; shared / fallback (used by the standard mag and any unlisted mag): + s_magazine_positions_magazin = 0,0,0 + s_magazine_rotations_magazin = 0,0,0 + s_magazine_scale_magazin = 1 + ; per-mag override for the extended mag only: + s_magazine_positions_magazin_mag_ak_5.45x39_extended = 0,-0.01,0 + s_magazine_scale_magazin_mag_ak_5.45x39_extended = 1 diff --git a/gamedata/scripts/scripted_magazines.script b/gamedata/scripts/scripted_magazines.script index 8825b9d..f95c582 100644 --- a/gamedata/scripts/scripted_magazines.script +++ b/gamedata/scripts/scripted_magazines.script @@ -219,7 +219,14 @@ function change_specific_mag_model(configs) add_bullets_to_magazines(obj, configs2) -- bullet swapping behaviour - local scale = ini_sys:r_float_ex(obj_sec, "s_magazine_scale_" .. parent_bone) or 1 + -- per-mag scale override (mirrors update_scripted_magazines): prefer the + -- mag-suffixed key so the swapped model lands on its final scale immediately + local scale_key = "s_magazine_scale_" .. parent_bone + local swap_sec = configs2.section + if swap_sec and ini_sys:r_float_ex(obj_sec, scale_key .. "_" .. swap_sec) then + scale_key = scale_key .. "_" .. swap_sec + end + local scale = ini_sys:r_float_ex(obj_sec, scale_key) or 1 attachment:set_scale(scale) end dbg("obj %s, bone_to_switch %s, model_path %s, name %s parent_bone %s", obj:name(), bone_to_switch, model_path, name, parent_bone) @@ -299,6 +306,23 @@ function update_scripted_magazines(obj, configs) -- iterates through each mag bone, not really necessary i just thought it would be useful when i first made it configs.skip_bone_name = configs.skip_bone_name or {} + + -- PER-MAG OFFSETS (backward compatible): for each offset field, prefer a key suffixed + -- with the magazine's section ("s_magazine___") and fall back + -- to the shared per-bone key ("s_magazine__"). Guns that only define the + -- shared keys are unaffected. The loaded mag is authoritative; during the mid-reload + -- swap it is still nil/old, so we fall back to the incoming/pending section dev already + -- captures, so the model lands on its final coords immediately instead of jumping. + local loaded_mag = get_mag_loaded(obj:id()) + local mag_section = (loaded_mag and loaded_mag.section) or incoming_mag_section or pending_swap_mag_section + local function mag_key(prefix, name) + local base = prefix .. name + if mag_section and ini_sys:r_string_ex(obj_section, base .. "_" .. mag_section) then + return base .. "_" .. mag_section + end + return base + end + for i,name in pairs(mag_bone_names) do if configs.skip_bone_name[name] then goto continue end @@ -318,24 +342,24 @@ function update_scripted_magazines(obj, configs) s_magazine:set_model(magazine_model_path, false) -- finds positions - local positions = ini_sys:r_string_ex(obj_section, "s_magazine_positions_" .. name) or "" + local positions = ini_sys:r_string_ex(obj_section, mag_key("s_magazine_positions_", name)) or "" positions = str_explode(positions, ",") for i,v in pairs(positions) do positions[i] = tonumber(v) end - + -- finds rotations - local rotations = ini_sys:r_string_ex(obj_section, "s_magazine_rotations_" .. name) or "" + local rotations = ini_sys:r_string_ex(obj_section, mag_key("s_magazine_rotations_", name)) or "" rotations = str_explode(rotations, ",") for i,v in pairs(rotations) do rotations[i] = tonumber(v) end - + -- finds scale - local scale = ini_sys:r_float_ex(obj_section, "s_magazine_scale_" .. name) or 1 - + local scale = ini_sys:r_float_ex(obj_section, mag_key("s_magazine_scale_", name)) or 1 + -- finds parent bone - local parent_bone = ini_sys:r_string_ex(obj_section, "s_magazine_parent_bone_" .. name) or "wpn_body" + local parent_bone = ini_sys:r_string_ex(obj_section, mag_key("s_magazine_parent_bone_", name)) or "wpn_body" -- sets everything necessary s_magazine:set_type(script_attachment_type.Hud) From a53ccda57985177d2dc4f060f2668e26388b3c8e Mon Sep 17 00:00:00 2001 From: Gamaun007 Date: Wed, 8 Jul 2026 17:21:56 +0200 Subject: [PATCH 2/3] Fix two reload-end visual glitches on the attachment 1. Skip set_model when the model path is unchanged (both the full refresh in update_scripted_magazines and the mid-reload swap in change_specific_mag_model): re-setting a live attachment's model resets its transform until the position/rotation setters re-apply, rendering one frame at the default pose. 2. Don't scale the attachment to 0 while a Mags Reloaded reload is still in progress: at reload end the idle anim triggers the no-mag refresh BEFORE the new mag data is committed (same frame), which hid the freshly inserted mag until the next animation change (movement). Co-Authored-By: Claude Opus 4.8 --- gamedata/scripts/scripted_magazines.script | 23 +++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/gamedata/scripts/scripted_magazines.script b/gamedata/scripts/scripted_magazines.script index f95c582..72a3724 100644 --- a/gamedata/scripts/scripted_magazines.script +++ b/gamedata/scripts/scripted_magazines.script @@ -209,7 +209,11 @@ function change_specific_mag_model(configs) -- this function returns an id local parent_bone = string.sub(name, 10) if parent_bone == bone_to_switch then - attachment:set_model(model_path, false) + -- only swap the model when it actually changed (see update_scripted_magazines): + -- re-setting a live attachment's model resets its transform for a frame + if attachment:get_model() ~= model_path then + attachment:set_model(model_path, false) + end -- bullet swapping behaviour local configs2 = {} @@ -275,8 +279,12 @@ function update_scripted_magazines(obj, configs) attachment:set_scale(0) end end - if configs.hide_override then - -- basically hide_override is true if the gun is doing an animation that ISNT reload. So if the gun is reloading we wont hide the magazine bone + -- basically hide_override is true if the gun is doing an animation that ISNT reload. So if the gun is reloading we wont hide the magazine bone + -- ...and not while a Mags Reloaded reload is still in progress: at reload end the + -- idle anim can trigger this refresh BEFORE the new mag data commits (same frame), + -- and scaling the attachment to 0 in that window hides the freshly inserted mag + -- until the next anim change (movement) + if configs.hide_override and not (magazines.action_in_progress and magazines.action_in_progress()) then obj:iterate_attachments(iterator) end @@ -338,8 +346,13 @@ function update_scripted_magazines(obj, configs) -- add_attachment can fail if not s_magazine then goto continue end - - s_magazine:set_model(magazine_model_path, false) + + -- only swap the model when it actually changed: set_model on a live attachment + -- resets its transform until the setters below re-apply, rendering one frame at + -- the default pose + if s_magazine:get_model() ~= magazine_model_path then + s_magazine:set_model(magazine_model_path, false) + end -- finds positions local positions = ini_sys:r_string_ex(obj_section, mag_key("s_magazine_positions_", name)) or "" From 703e3dcdab67ce282e55bd4449c28f31f5e838d7 Mon Sep 17 00:00:00 2001 From: Gamaun007 Date: Thu, 9 Jul 2026 21:21:51 +0200 Subject: [PATCH 3/3] Per-mag swap: also set position/rotation, not just scale change_specific_mag_model swapped the model (and scale) mid-reload but left position/rotation at the previous mag's pose until the next idle refresh, so a mag could sit visibly wrong during the reload. Apply the per-mag position/rotation there too (same suffixed-key resolution), so the swapped model lands on its final pose immediately - matching the full refresh in update_scripted_magazines. Co-Authored-By: Claude Opus 4.8 --- gamedata/scripts/scripted_magazines.script | 27 +++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/gamedata/scripts/scripted_magazines.script b/gamedata/scripts/scripted_magazines.script index 72a3724..12eea28 100644 --- a/gamedata/scripts/scripted_magazines.script +++ b/gamedata/scripts/scripted_magazines.script @@ -223,14 +223,29 @@ function change_specific_mag_model(configs) add_bullets_to_magazines(obj, configs2) -- bullet swapping behaviour - -- per-mag scale override (mirrors update_scripted_magazines): prefer the - -- mag-suffixed key so the swapped model lands on its final scale immediately - local scale_key = "s_magazine_scale_" .. parent_bone + -- per-mag offsets (mirrors update_scripted_magazines): prefer the mag-suffixed + -- key so the swapped model lands on its FINAL position/rotation/scale immediately. + -- Without setting position/rotation here the model swaps but keeps the previous + -- mag's pose until the next idle refresh - visibly wrong mid-reload. local swap_sec = configs2.section - if swap_sec and ini_sys:r_float_ex(obj_sec, scale_key .. "_" .. swap_sec) then - scale_key = scale_key .. "_" .. swap_sec + local function swap_key(prefix) + local base = prefix .. parent_bone + if swap_sec and ini_sys:r_string_ex(obj_sec, base .. "_" .. swap_sec) then + return base .. "_" .. swap_sec + end + return base end - local scale = ini_sys:r_float_ex(obj_sec, scale_key) or 1 + + local positions = str_explode(ini_sys:r_string_ex(obj_sec, swap_key("s_magazine_positions_")) or "", ",") + for i,v in pairs(positions) do positions[i] = tonumber(v) end + + local rotations = str_explode(ini_sys:r_string_ex(obj_sec, swap_key("s_magazine_rotations_")) or "", ",") + for i,v in pairs(rotations) do rotations[i] = tonumber(v) end + + local scale = ini_sys:r_float_ex(obj_sec, swap_key("s_magazine_scale_")) or 1 + + attachment:set_position(positions[1], positions[2], positions[3]) + attachment:set_rotation(rotations[1], rotations[2], rotations[3]) attachment:set_scale(scale) end dbg("obj %s, bone_to_switch %s, model_path %s, name %s parent_bone %s", obj:name(), bone_to_switch, model_path, name, parent_bone)