<?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
    -- "Voice only" was removed from the creation menu (it produced the
    -- exact same trigger flags as "all" anyway - the distinction was
    -- never actually implemented). Kept here so any gag saved with this
    -- method before the change still loads and behaves the same.
    flags = flags + 4  -- OmitFromOutput
  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 = {
    "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" ..
    "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 == "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>
