MrxShootingGallery
Module: mrxshootinggallery.lua
Overview
The MrxShootingGallery module drives the “shooting gallery” minor-contract scenario (the one that hands you a gallery weapon inside a bounded firing range). It temporarily strips the player’s normal weapons and stows them below the character, sets up an invisible boundary around the range, and toggles each character’s firelock (weapon-firing disable) as they step in and out of that boundary. When the mission ends it hands the original weapons back.
Inheritance
- Inherits from:
none - Imports:
MrxSubtitle,MrxVoSequence
MrxSubtitle is imported but not referenced in this file; only MrxVoSequence.Start is actually called (the firelock warning VO).
Instance pattern
Stateless module — all state lives in module-level globals, not a per-instance table. The event handles it stores (_BorderEventP1, _BorderEventP2, uFireLockVO, tWeapons, uPrimaryWeapon, _evNetSafeSetupBorder) are plain globals; only _evPlayerJoined is declared local. This means a single active gallery at a time is assumed — there is no tInstance[uGuid] bookkeeping.
Functions
RemoveWeapons(uPlayer)
Drops the player’s primary and secondary weapons, disables their physics, and teleports each one to 5 units below the player (Object.SetPosition(w, x, y - 5, z)) so they are out of reach for the duration. Returns a tWeapons table with keys Primary1, Primary2, Secondary1, Secondary2 (any of which may be nil). Note it re-reads Human.Inventory.GetPrimaryWeapon a second time after dropping the first (and likewise for secondary) to catch a second held weapon — the two reads are against the live inventory, so what lands in Primary2 is whatever is primary after Primary1 was dropped.
ReturnWeapons(uPlayer, tWeapons)
Turns off infinite ammo (Object.SetInfiniteAmmo(uPlayer, false)), drops the temporary gallery weapon the player is holding, then re-equips the saved weapons in reverse order (Secondary1, Secondary2, Primary2, Primary1) so Primary1 ends up as the active weapon.
ClearEvents()
Deletes all registered events related to the shooting gallery mission, ensuring no lingering event handlers remain.
Reset()
Resets the firelock states for both primary and secondary characters and clears any existing events. This function is called when resetting the mission state.
NetSafeSetupBorder(uBorderName)
Sets up the border for the shooting gallery on the client side if the player is not local. It creates an event to call SetupBorder when the game state changes to “WaitForTether”.
SetupClientBorder(uBorderName)
Sets up boundary events for the secondary character on the client side. This function ensures that firelock states are managed correctly when the player exits or enters the designated area.
SetupBorder(uBorderName)
Sets up the shooting gallery border on both server and client sides. It configures firelock states, creates boundary events, and handles player join events to ensure proper mission setup.
SteppedOut(uChar, uBorderName)
Called when a character exits the designated boundary area. This function sets the firelock state to true for the character and starts a voice sequence warning about restricted weapon usage. Contains an empty if Player.GetLocalCharacter() == uChar then end block with no body — likely lost/stripped logic from decompilation, has no effect either way.
SteppedIn(uChar, uBorderName)
Called when a character enters the designated boundary area. This function sets the firelock state to false for the character and re-creates boundary events to manage further transitions.
Events
All subscriptions below are real Event.Create/Event.CreatePersistent calls.
Event.GameStateChangefiltered on"WaitForTether", "exit"(viaNetSafeSetupBorder, handle_evNetSafeSetupBorder) — re-runsSetupBorder(uBorderName)once the tether wait state exits, client-only (Net.IsClient()gate).Event.ObjectHibernationfiltered on"awake"(viaSetupClientBorder) — one-shot, waits for the secondary character to wake before wiring up_BorderEventP2.Event.ScriptEventnamed"mpPlayerJoin"(persistent, viaSetupBorder, handle_evPlayerJoined) — guarded to only fire whenNet.IsServer()and the joining player is not local; callsSetupClientBorder.Event.Boundary(viaSetupBorder/SteppedOut/SteppedIn, handles_BorderEventP1/_BorderEventP2) — fires on"exit"/"enter"ofuBorderNamefor the primary/secondary character;SteppedOutandSteppedInre-create each other’s opposite-direction listener every time they fire, forming a ping-pong chain.Event.WeaponEventfiltered on"FireLock"for the primary weapon (viaSteppedOut, handleuFireLockVO) — triggersMrxVoSequence.Startwith the VO line"Fiona-In-Mission-MinorContract-Pmc31-08"when the locked weapon is fired.
Module constants & tunables
- VO line on firelock:
"Fiona-In-Mission-MinorContract-Pmc31-08"— theMrxVoSequence.Startbark played when a firelocked weapon is pulled outside the boundary (via theEvent.WeaponEventhandler inSteppedOut). Swap this string to change the “you can’t shoot here” warning. - Drop depth: weapons are stashed at
y - 5(5 world units below the player). No other magic numbers or template names in this file. - The boundary object itself is passed in as
uBorderName(a named world region); this module does not create it — it only subscribes toEvent.Boundaryon it.
Notes for modders
- All the mission wiring is overridable — every function here is a plain global (not
local), so a mod can replaceSteppedOut/SteppedInto change what firelock/VO behavior happens at the boundary. See Function override. - The boundary handlers are self-rearming:
SteppedOutdeletes the current"exit"listener and creates the opposite"enter"listener viaSteppedIn, and vice-versa. If you add work in either, keep that delete/recreate pair intact or the ping-pong chain breaks. Human.SetFireLock(uChar, bLock)is the actual firing gate —truelocks,falseunlocks.Reset()unlocks both characters and tears down all events; call it (or let theuBorderName == nilpath ofSetupBordercall it) to fully clean up.- The event handles are module globals, so only one gallery boundary can be active at a time. Two concurrent
SetupBordercalls would clobber_BorderEventP1/_BorderEventP2and leak the previous listeners. - Client/host split:
SetupBorderruns the server-authoritativeNet.SetShootingGalleryBorderand wires the"mpPlayerJoin"ScriptEvent;NetSafeSetupBorderandSetupClientBorderhandle the client/joining-player side. SeeNet.