Files
Mush-Soundpack/cosmic rage/worlds/plugins/GagManager.xml
OlegTheSnowman c3d85360c1 Remap Gag Manager from Alt+N to Alt+F2
Alt+N conflicted with direction_plugin.xml's south-west movement key
and was silently shadowing it (Gag Manager loads later, so its binding
won). Alt+F2 is unused. Updated the accelerator, the config dialog
title, and the readme accordingly.
2026-07-11 00:09:58 +03:00

395 lines
11 KiB
XML

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
name="GagManager"
author="Antigravity"
id="d2211ef7d54c4a7bd0cc9ab8"
save_state="y"
language="Lua"
purpose="VIPMud-like accessible gagging plugin."
requires="4.46"
version="1.1"
>
</plugin>
<!-- Aliases -->
<aliases>
<alias
match="gagconfig"
enabled="y"
script="gagconfig"
omit_from_output="y"
omit_from_log="y"
omit_from_command_history="y"
send_to="12"
sequence="100"
>
</alias>
</aliases>
<!-- Script -->
<script>
<![CDATA[
require "serialize"
dir = world.GetInfo(67)
gags = {}
subst_map = {}
function OnPluginInstall()
-- Register alt+f2 to open gag configuration menu (was alt+n, which
-- conflicted with direction_plugin.xml's south-west movement key)
Accelerator("alt+f2", "gagconfig")
load_gags()
register_all_gags()
end
function OnPluginClose()
end
function gags_file_path()
-- Store gags as a human-readable file next to the world file so users can copy/share them
return dir .. "gags.lua"
end
function save_gags()
-- Save to MUSHclient variable (for fast load)
local serialized = serialize.save_simple(gags)
SetVariable("gags_data", serialized)
-- Also write a plain file next to the world that users can copy/share
local f = io.open(gags_file_path(), "w")
if f then
f:write("-- Cosmic Rage Mush Soundpack - Gag Manager data\n")
f:write("-- Copy this file to share your gags with others.\n")
f:write("-- Place it next to your cosmic rage.MCL file to load it.\n\n")
f:write("return " .. serialized .. "\n")
f:close()
end
end
function load_gags()
-- Try loading from file first (portable, shareable)
local f = io.open(gags_file_path(), "r")
if f then
local content = f:read("*all")
f:close()
local chunk, err = loadstring(content)
if chunk then
local ok, result = pcall(chunk)
if ok and type(result) == "table" then
gags = result
end
end
end
if not gags or gags == {} then
-- Fall back to MUSHclient variable
local serialized = GetVariable("gags_data")
if serialized and serialized ~= "" then
local chunk, err = loadstring("return " .. serialized)
if chunk then
local ok, result = pcall(chunk)
if ok then gags = result end
end
end
end
gags = gags or {}
-- Migrate old entries (from before each gag had a stable trigger name) and
-- make sure every gag has a unique trigger name we can add/delete directly.
local next_id = 1
for _, gag in ipairs(gags) do
if gag.name then
local n = tonumber(gag.name:match("gag_(%d+)"))
if n and n >= next_id then next_id = n + 1 end
end
end
for _, gag in ipairs(gags) do
if not gag.name then
gag.name = "gag_" .. tostring(next_id)
next_id = next_id + 1
end
end
end
-- Adds (or replaces) the MUSHclient trigger for a single gag entry.
function add_trigger_for_gag(gag)
-- Enabled=1, KeepEvaluating=8, Replace=1024, Temporary=16384
local flags = 1 + 8 + 1024 + 16384
local script_name = ""
if gag.method == "all" then
flags = flags + 4 -- OmitFromOutput
elseif gag.method == "voice" then
flags = flags + 4 -- OmitFromOutput: hidden from screen, but MushReader still speaks it
elseif gag.method == "substitute" then
flags = flags + 4 -- OmitFromOutput: hide the original line
script_name = "substitute_trigger"
end
-- No regex needed - MUSHclient's normal trigger matching already treats
-- "*" as a wildcard, so the gag pattern can be used as-is.
local res = AddTrigger(gag.name, gag.line, "", flags, -1, 0, "", script_name)
if res == 0 then
SetTriggerOption(gag.name, "group", gag.file)
if gag.method == "substitute" then
SetTriggerOption(gag.name, "send_to", "14")
subst_map[gag.name] = gag.subst
end
end
return res
end
function register_all_gags()
subst_map = {}
for _, gag in ipairs(gags) do
add_trigger_for_gag(gag)
end
-- Apply enabled/disabled states to trigger groups
local groups = {"ActivityGags", "ShipGags", "VehicleGags", "MiscGags"}
for _, g in ipairs(groups) do
local state = GetVariable(g .. "_state") or "enabled"
EnableTriggerGroup(g, state == "enabled")
end
end
-- --- TRIGGER CALLBACKS ---
function substitute_trigger(name, line, wildcards)
local subst = subst_map[name]
if subst then
local res = subst
for i = 1, #wildcards do
res = res:gsub("%%" .. tostring(i), wildcards[i])
end
res = res:gsub("%%0", line)
Note(res) -- original line is already omitted; print the replacement
end
end
-- --- GAG CONFIGURATION DIALOGS ---
local function sleep(s)
local t = os.clock()
while os.clock() - t < s do end
end
function toggle_group(group_name)
sleep(0.2)
local current = GetVariable(group_name .. "_state") or "enabled"
local new_state = "disabled"
if current == "disabled" then
new_state = "enabled"
end
SetVariable(group_name .. "_state", new_state)
EnableTriggerGroup(group_name, new_state == "enabled")
Execute("tts_interrupt " .. group_name .. " set to " .. new_state)
end
function delete_gag()
sleep(0.2)
if #gags == 0 then
Execute("tts_interrupt No gags defined yet.")
return
end
local choices = {}
for i, gag in ipairs(gags) do
table.insert(choices, string.format("[%s] %s (%s)", gag.file, gag.line, gag.method))
end
local result = utils.choose("Select a gag to delete:", "Delete Gag", choices)
if result then
local idx
if type(result) == "number" then
idx = result
else
for i, v in ipairs(choices) do
if v == result then idx = i; break end
end
end
if idx then
local gag = gags[idx]
DeleteTrigger(gag.name)
subst_map[gag.name] = nil
table.remove(gags, idx)
save_gags()
Execute("tts_interrupt Gag deleted: " .. gag.line)
end
end
end
function next_gag_name()
local max_id = 0
for _, gag in ipairs(gags) do
local n = tonumber(gag.name:match("gag_(%d+)"))
if n and n > max_id then max_id = n end
end
return "gag_" .. tostring(max_id + 1)
end
function addnewgag()
sleep(0.2)
-- Prompt 1: Line to gag
local gag_line = utils.inputbox(
"Enter the exact text you want to gag, as it appears in the game.\n" ..
"\n" ..
"You can use * (asterisk) as a wildcard to match any text.\n" ..
"Examples:\n" ..
" warrior hugs you -- gags that exact line\n" ..
" * hugs you -- gags anyone hugging you\n" ..
" You receive * -- gags any 'You receive ...' line",
"Step 1 of 3: Enter gag pattern", "")
if not gag_line or gag_line == "" then
return
end
sleep(0.2)
-- Prompt 2: Gag method
local m_choices = {
"Voice only - Hide from screen, still spoken by screen reader",
"Full gag - Hide completely from screen and speech",
"Substitute - Replace the matched line with different text"
}
local m_res = utils.choose(
"How should this line be gagged?\n" ..
"\n" ..
"Voice only: The line is hidden from the screen and log, but your screen reader will still read it aloud. Good for information you want to hear but don't need cluttering the screen.\n" ..
"Full gag: The line is completely removed from both the screen and speech. Good for things you never want to see.\n" ..
"Substitute: Replaces the matched line with your own custom text.",
"Step 2 of 3: Choose gag method", m_choices)
if not m_res then
return
end
if type(m_res) == "number" then
m_res = m_choices[m_res]
end
local gag_method = ""
local gag_sub = ""
if m_res == "Voice only - Hide from screen, still spoken by screen reader" then
gag_method = "voice"
elseif m_res == "Full gag - Hide completely from screen and speech" then
gag_method = "all"
elseif m_res == "Substitute - Replace the matched line with different text" then
gag_method = "substitute"
sleep(0.2)
gag_sub = utils.inputbox(
"Enter the replacement text that will be shown instead of the matched line.\n" ..
"\n" ..
"You can use placeholders:\n" ..
" %0 - The entire original matched line\n" ..
" %1 - The first wildcard * match\n" ..
" %2 - The second wildcard * match, and so on\n" ..
"\n" ..
"Example: If your gag pattern is '* hugs you'\n" ..
"and you type '%1 pokes you', it shows 'warrior pokes you' instead.",
"Substitution text", "")
if not gag_sub or gag_sub == "" then
return
end
end
sleep(0.2)
-- Prompt 3: Category
local f_choices = {
"Activity Gags - Mining, crafting, harvesting, general activities",
"Ship Gags - Starship combat, travel, ship systems",
"Miscellaneous Gags - Everything else that does not fit above",
"Vehicle Gags - Ground vehicles, speeders, walkers"
}
local f_res = utils.choose(
"Which category should this gag be stored in?\n" ..
"\n" ..
"Categories let you toggle entire groups of gags on or off at once from the main menu.\n" ..
"For example, you can disable all Ship Gags when not flying, and re-enable them when you board a ship.",
"Step 3 of 3: Choose category", f_choices)
if not f_res then
return
end
if type(f_res) == "number" then
f_res = f_choices[f_res]
end
local gag_file = ""
if f_res == "Activity Gags - Mining, crafting, harvesting, general activities" then
gag_file = "ActivityGags"
elseif f_res == "Ship Gags - Starship combat, travel, ship systems" then
gag_file = "ShipGags"
elseif f_res == "Miscellaneous Gags - Everything else that does not fit above" then
gag_file = "MiscGags"
elseif f_res == "Vehicle Gags - Ground vehicles, speeders, walkers" then
gag_file = "VehicleGags"
end
local gag = {
name = next_gag_name(),
line = gag_line,
method = gag_method,
subst = gag_sub,
file = gag_file
}
local res = add_trigger_for_gag(gag)
if res ~= 0 then
Execute("tts_interrupt Could not create that gag, error " .. tostring(res))
return
end
table.insert(gags, gag)
save_gags()
Execute("tts_interrupt Gag added successfully!")
end
function gagconfig()
repeat
sleep(0.2)
local choices = {
"Create a new gag...",
"Delete an existing gag...",
"Toggle Activity-related gags (" .. (GetVariable("ActivityGags_state") or "enabled") .. ")",
"Toggle Ship-related gags (" .. (GetVariable("ShipGags_state") or "enabled") .. ")",
"Toggle Vehicle-related gags (" .. (GetVariable("VehicleGags_state") or "enabled") .. ")",
"Toggle Misc Gags (" .. (GetVariable("MiscGags_state") or "enabled") .. ")"
}
local result = utils.choose("Choose a gag option to edit or toggle:", "Gag Configuration (Alt+F2)", choices)
if not result then break end
-- utils.choose may return either an index (number) or the string itself
local idx
if type(result) == "number" then
idx = result
else
for i, v in ipairs(choices) do
if v == result then idx = i; break end
end
end
if idx == 1 then
addnewgag()
elseif idx == 2 then
delete_gag()
elseif idx == 3 then
toggle_group("ActivityGags")
elseif idx == 4 then
toggle_group("ShipGags")
elseif idx == 5 then
toggle_group("VehicleGags")
elseif idx == 6 then
toggle_group("MiscGags")
end
until false
end
]]>
</script>
</muclient>