Fix accessible output window, revert Tab/Ctrl+Space, sync readme keys

- output_functions.xml: merge NotepadSmartScroll's Win32/Alien-based
  smart-scroll logic in directly (was a separate plugin). Fixed the
  caret-restore bug: gating the restore on scroll position was wrong
  since the notepad tracks near the bottom during normal reading; now
  gated on whether the caret was literally at the end of the old text.
  Added a jump-to-end-on-focus toggle (Ctrl+Alt+J) with a 0.2s polling
  timer to catch focus landing on the output notepad by any means.
- Reverted the Tab/Ctrl+Space rebinding - removed switch_to_output and
  trigger_word_completion along with their accelerators.
- cosmic rage.MCL: dropped the dangling NotepadSmartScroll.xml include
  (file no longer exists, was reintroduced by an auto-save).
- readme.md: documented the actual current CosmicRage.xml keys (several
  had drifted out of sync - Alt+F12 is now stop-sounds not restart,
  Alt+Delete/Ctrl+S/Ctrl+F1-3/Alt+F3/Alt+Shift+H/F11 were undocumented)
  and added the output_functions.xml accessible-output keybindings,
  which weren't documented at all before.
This commit is contained in:
OlegTheSnowman
2026-07-11 00:02:28 +03:00
parent 21fa3e0d57
commit 09785a9080
8 changed files with 719 additions and 155 deletions

View File

@@ -25,7 +25,7 @@ sequence="10000"
for i = 1, 9 do
Accelerator("alt + " .. i, "@buffer " .. i)
end
Accelerator("alt + 0", "@buffer 10") -- 10 is a special case
Accelerator("alt + 0", "@buffer 10")
Accelerator("alt + right", "@buffer forward")
Accelerator("alt + left", "@buffer backward")
Accelerator("alt + down", "@buffer down")
@@ -34,41 +34,64 @@ Accelerator("alt + pagedown", "@buffer scrolldown")
Accelerator("alt + pageup", "@buffer scrollup")
Accelerator("alt + end", "@buffer bottom")
Accelerator("alt + home", "@buffer top")
Accelerator("alt+shift+delete", "@buffer clear")
Accelerator("alt+delete", "@buffer clear")
Accelerator("alt+shift+delete", "@buffer clearall")
Accelerator("alt+shift+right", "@buffer add")
Accelerator("alt+shift+left", "@buffer delete")
Accelerator("f4", "@sp-settings master volume")
Accelerator("f5", "@sp-settings General volume")
Accelerator("f6", "@sp-settings ambiance volume")
Accelerator("f7", "@sp-settings in and out of character communications volume")
Accelerator("f8", "@sp-settings planetary music volume")
Accelerator("ctrl+f5", "@sp-settings clocks volume")
Accelerator("ctrl+f6", "@sp-settings Combat music volume")
Accelerator("ctrl+f7", "@sp-settings IC social volume")
Accelerator("ctrl+f8", "@sp-settings OOC social volume")
Accelerator("alt+f5", "@sp-settings Ground Combat volume")
Accelerator("alt+f6", "@sp-settings harvesting activity volume")
Accelerator("alt+f7", "@sp-settings Salvage activity volume")
Accelerator("alt+f8", "@sp-settings volume for games such as Pilot's Parody")
Accelerator("shift+f5", "@sp-settings starship combat volume")
Accelerator("shift+f6", "@sp-settings ship volume")
Accelerator("shift+f7", "@sp-settings ship movement volume")
Accelerator("shift+f8", "@sp-settings vehicle volume")
Accelerator("f9", "@sp-settings IC social sounds")
Accelerator("f10", "@sp-settings OOC social sounds")
Accelerator("alt+f9", "@sp-settings panning of sounds")
Accelerator("alt+f10", "@sp-settings pitch-shifting sounds")
Accelerator("alt+f11", "@sp-settings ambiance")
Accelerator("ctrl+f9", "@sp-settings planetary music")
Accelerator("ctrl+f10", "@sp-settings space combat music")
Accelerator("ctrl+f11", "@sp-settings weather sounds")
Accelerator("alt+f12", "@sp-restart")
Accelerator("shift+alt+f9", "@sp-settings Interrupt OOC Social sounds when the next one plays")
Accelerator("f2", "@sp-settings")
Accelerator("f5", "@sp-settings General volume")
Accelerator("f6", "@sp-settings ambiance volume")
Accelerator("f7", "@sp-settings in and out of character communications volume")
Accelerator("f8", "@sp-settings planetary music volume")
Accelerator("ctrl+f5", "@sp-settings clocks volume")
Accelerator("ctrl+f6", "@sp-settings Combat music volume")
Accelerator("ctrl+f7", "@sp-settings IC social volume")
Accelerator("ctrl+f8", "@sp-settings OOC social volume")
Accelerator("alt+f5", "@sp-settings Ground Combat volume")
Accelerator("alt+f6", "@sp-settings harvesting activity volume")
Accelerator("alt+f7", "@sp-settings Salvage activity volume")
Accelerator("alt+f8", "@sp-settings volume for games such as Pilot's Parody")
Accelerator("shift+f5", "@sp-settings starship combat volume")
Accelerator("shift+f6", "@sp-settings ship volume")
Accelerator("shift+f7", "@sp-settings ship movement volume")
Accelerator("shift+f8", "@sp-settings vehicle volume")
Accelerator("f9", "@sp-settings IC social sounds")
Accelerator("f10", "@sp-settings OOC social sounds")
Accelerator("alt+f9", "@sp-settings panning of sounds")
Accelerator("alt+f10", "@sp-settings pitch-shifting sounds")
Accelerator("alt+f11", "@sp-settings ambiance")
Accelerator("ctrl+f9", "@sp-settings planetary music")
Accelerator("ctrl+f10", "@sp-settings space combat music")
Accelerator("ctrl+f11", "@sp-settings weather sounds")
Accelerator("shift+alt+f9", "@sp-settings Interrupt OOC Social sounds when the next one plays")
Accelerator("f2", "@sp-settings")
Accelerator("shift+f1", "@sp-toggle")
Accelerator("Ctrl+shift+enter", "@sp-settings buffer sounds")
Accelerator("ctrl+shift+v", "@sp-version")
Accelerator("ctrl+s", "save_state_func")
Accelerator("alt+shift+h", "localsphelp")
Accelerator("ctrl+f2", "mainvoldown")
Accelerator("ctrl+f3", "mainvolup")
Accelerator("alt+f3", "intromusictoggle")
Accelerator("ctrl+f1", "smute")
Accelerator("f11", "f11_stop")
Accelerator("alt+f12", "alt_f12_stop")
function save_state_func()
SaveState()
Execute("tts_interrupt Client-side Soundpack Settings have been saved successfully!")
end
function f11_stop()
ppi.stop(0)
Execute("tts_interrupt All currently playing sounds have been stopped.")
Send("@sp-restart")
end
function alt_f12_stop()
ppi.stop(0)
Execute("tts_interrupt All currently playing sounds have been stopped.")
end
function RegisterMushClient(name, line, wildcards)
@@ -91,7 +114,15 @@ Note("You are currently running version " .. tostring(CURRENT_SP_VERSION).." of
if version_diff > 0 then
ColourNote("yellow", "", "Warning: Your soundpack is " .. version_diff .. " version(s) out of date. We are now on version " .. user_version .. " of the soundpack.")
ColourNote("yellow", "", "Run Updater.bat to pull the latest version.")
ColourNote("yellow", "", "Launching the updater...")
ppi.play(dir.."sounds/ogg/general/comms/announcement.ogg", 0, 0, 100)
local bat_path = dir .. "..\\updator.bat"
-- Signature is (filename, params, defdir, operation, show) - this was
-- previously passing "open" as the filename, so it never actually
-- launched cmd.exe/updator.bat at all.
utils.shellexecute("cmd", '/c "' .. bat_path .. '"', "", "open", 1)
ColourNote("yellow", "", "The updater has been launched in a separate window. Once the update is complete, please restart MUSHclient to apply the updates!")
Execute("tts_interrupt Warning: Your soundpack is out of date. The updater has been launched. Once the update is complete, please restart MUSHclient to apply the updates!")
else
ColourNote("green", "", "Your soundpack is up to date!")
end
@@ -102,6 +133,109 @@ function url_encode(str)
return string.format("%%%02X", string.byte(c))
end))
end
pending_downloads = {}
pending_play = {}
function download_sound(path, action, pan, volume, pitch, id)
if pending_downloads[path] then
table.insert(pending_play[path], {action=action, pan=pan, volume=volume, pitch=pitch, id=id})
return
end
pending_downloads[path] = os.time()
pending_play[path] = { {action=action, pan=pan, volume=volume, pitch=pitch, id=id} }
local sound_path = dir .. "sounds/ogg/" .. path .. ".ogg"
local safe_path = sound_path:gsub("/", "\\")
local parent_dir = safe_path:match("^(.+)\\[^\\]+$")
local encoded_path = url_encode(path)
local url = "http://nathantech.net:3000/CosmicRage/CosmicRageSounds/raw/branch/main/ogg/" .. encoded_path .. ".ogg"
-- Download to a .part file and only rename it to the real filename after
-- curl succeeds, so check_downloads() can never mistake a half-written or
-- failed transfer for a completed one. Clears any leftover .err first, and
-- deletes it again on success, so a stale error/empty file can't linger.
local cmd_args = string.format(
'/C mkdir "%s" 2>nul & del /q "%s.err" 2>nul & ' ..
'curl.exe -s -S -L -f -o "%s.part" "%s" 2>"%s.err" ' ..
'&& (move /y "%s.part" "%s" >nul & del /q "%s.err" 2>nul) || del /q "%s.part" 2>nul',
parent_dir, safe_path, safe_path, url, safe_path, safe_path, safe_path, safe_path, safe_path
)
-- NOTE: correct shellexecute signature is (filename, params, defdir, operation, show)
utils.shellexecute("cmd", cmd_args, "", "open", 0)
end
-- If a download hasn't finished or failed within this long, give up on it
-- rather than let a stuck curl process silently block that sound forever.
DOWNLOAD_TIMEOUT_SECONDS = 30
function check_downloads(name)
for path, started_at in pairs(pending_downloads) do
local sound_path = dir .. "sounds/ogg/" .. path .. ".ogg"
local safe_path = sound_path:gsub("/", "\\")
local f = io.open(sound_path, "r")
if f then
f:close()
local queue = pending_play[path]
pending_downloads[path] = nil
pending_play[path] = nil
if queue then
for _, p in ipairs(queue) do
play_sound_now(path, p.action, p.pan, p.volume, p.pitch, p.id)
end
end
else
local ef = io.open(safe_path .. ".err", "r")
if ef then
local msg = ef:read("*all")
ef:close()
if msg and msg ~= "" then
os.remove(safe_path .. ".err")
pending_downloads[path] = nil
pending_play[path] = nil
Note("Soundpack: download failed for " .. path .. ": " .. msg:gsub("[\r\n]", ""))
end
elseif os.time() - started_at > DOWNLOAD_TIMEOUT_SECONDS then
pending_downloads[path] = nil
pending_play[path] = nil
Note("Soundpack: download timed out for " .. path)
end
end
end
end
function play_sound_now(path, action, pan, volume, pitch, id)
local sound_file = dir.."sounds/ogg/"..path..".ogg"
local temp
if (action == "play") then
temp = ppi.play(sound_file, 0, pan, volume)
if (pitch > -10000) then
ppi.setPitch(pitch, temp)
end
if (pan > -10000) then
ppi.setPan(pan, temp)
end
elseif (action == "playrec") then
temp = ppi.play(sound_file, 0, pan, volume)
SetVariable(id, temp)
if (pitch > -10000) then
ppi.setPitch(pitch, temp)
end
if (pan > -10000) then
ppi.setPan(pan, temp)
end
elseif (action == "loop") then
temp = ppi.play(sound_file, 1, pan, volume)
SetVariable(id, temp)
if (pitch > -10000) then
ppi.setPitch(pitch, temp)
end
if (pan > -10000) then
ppi.setPan(pan, temp)
end
end
end
]]>
@@ -197,106 +331,87 @@ match="^\$sphook\s+(?P<action>[\w]+):(?P<path>[-\w./\\\s]+):(?P<volume>[\w]+):(?
pan="%5"
id="%6"
volume=tonumber(volume)
if (path == "na") then
else
sound_path = dir .. "sounds/ogg/" .. path .. ".ogg"
directory_path = sound_path:match("(.*/)")
if directory_path then
test_file = io.open(directory_path .. "tester.txt", "w")
if test_file then
test_file:close()
os.remove(directory_path .. "checker.txt") -- Clean up
if (pitch == "na") then
pitch = -10000
else
os.execute('mkdir "' .. directory_path .. '" 2>nul')
pitch = tonumber(pitch)
pitch = (pitch - 44100) / 98
end
end
file = io.open(sound_path, "r")
if file then
file:close()
else
local encoded_path = url_encode(path)
local download_url = "http://nathantech.net:3000/CosmicRage/CosmicRageSounds/raw/branch/main/ogg/" .. encoded_path .. ".ogg"
body = {}
result, status_code, headers, status = socket.http.request{
url = download_url, sink = ltn12.sink.table(body)
}
if result and status_code == 200 then
save_file = io.open(sound_path, "wb")
if save_file then
save_file:write(table.concat(body))
save_file:close()
if (pan == "na") then
pan = -10000
else
pan = tonumber(pan)
end
if (path == "na") then
if (action == "adjustsound") then
temp = GetVariable(id)
if volume and volume > -1 then
ppi.setVol(volume, temp)
end
if (pitch > -10000) then
ppi.setPitch(pitch, temp)
end
if (pan > -10000) then
ppi.setPan(pan, temp)
end
elseif (action == "stop") then
temp = GetVariable(id)
if temp then
ppi.stop(temp)
end
end
else
local sound_path = dir .. "sounds/ogg/" .. path .. ".ogg"
local file = io.open(sound_path, "r")
if file then
file:close()
play_sound_now(path, action, pan, volume, pitch, id)
else
download_sound(path, action, pan, volume, pitch, id)
end
else
Note("Download of "..path.." failed with status code: " .. tostring(status_code))
end
end
end
if (pitch == "na") then
pitch = -10000
else
pitch = tonumber(pitch)
pitch = (pitch - 44100) / 98
end
if (pan == "na") then
pan = -10000
else
pan = tonumber(pan)
end
if (action == "play") then
temp = ppi.play(dir.."sounds/ogg/"..path..".ogg", 0, pan, volume)
if (pitch > -10000) then
ppi.setPitch(pitch, temp)
end
if (pan > -10000) then
ppi.setPan(pan, temp)
end
end
if (action=="playrec") then
temp=ppi.play(dir.."sounds/ogg/"..path..".ogg", 0, pan, volume)
SetVariable(id, temp)
if(pitch>-10000) then
ppi.setPitch(pitch,temp)
end
if(pan>-10000) then
ppi.setPan(pan,temp)
end
end
if (action=="loop") then
temp=ppi.play(dir.."sounds/ogg/"..path..".ogg", 1, pan, volume)
SetVariable(id, temp)
if(pitch>-10000) then
ppi.setPitch(pitch,temp)
end
if(pan>-10000) then
ppi.setPan(pan,temp)
end
end
if (action=="adjustsound") then
temp=GetVariable (id)
if(volume>-1) then
ppi.setVol(volume,temp)
end
if(pitch>-10000) then
ppi.setPitch(pitch,temp)
end
if(pan>-10000) then
ppi.setPan(pan,temp)
end
end
if (action=="stop") then
temp=GetVariable(id)
ppi.stop(temp)
end
</send>
</trigger>
</triggers>
<!-- Aliases -->
<!-- Timers -->
<timers>
<timer
enabled="y"
second="0.5"
script="check_downloads"
>
</timer>
</timers>
<aliases>
<alias
match="f11_stop"
enabled="y"
script="f11_stop"
omit_from_output="y"
omit_from_log="y"
omit_from_command_history="y"
send_to="12"
sequence="100"
>
</alias>
<alias
match="alt_f12_stop"
enabled="y"
script="alt_f12_stop"
omit_from_output="y"
omit_from_log="y"
omit_from_command_history="y"
send_to="12"
sequence="100"
>
</alias>
</aliases>
</muclient>