Created
October 23, 2023 00:22
-
-
Save chadac/339c2f8a9239e793d209af595282ffa5 to your computer and use it in GitHub Desktop.
Transformice Speedrun Module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
catData = { | |
["vanilla%"] = { | |
description = "All vanilla maps that can be completed by a single player. Plays as shaman only when needed.", | |
sham = false, --default sham settings | |
maps = { | |
{id="0",sham=true}, {id="1"}, {id="2"}, {id="3",sham=true}, | |
{id="4",sham=true}, {id="5",sham=true}, {id="6"}, {id="7"}, | |
{id="8"}, {id="9",sham=true}, {id="10"}, {id="11"}, | |
{id="12"}, {id="13"}, {id="14"}, {id="15",sham=true}, | |
{id="16",sham=true}, {id="17",sham=true}, | |
{id="18",sham=true}, {id="19"}, {id="20"}, | |
{id="21",sham=true}, {id="22"}, {id="23"}, {id="24"}, | |
{id="25",sham=true}, {id="26"}, {id="27"}, {id="28"}, | |
{id="30"}, {id="31"}, {id="32",sham=true}, | |
{id="34",sham=true}, {id="35"}, {id="36",sham=true}, | |
{id="37"}, {id="38",sham=true}, {id="39",sham=true}, | |
{id="40"}, {id="41"}, {id="42"}, {id="43"}, {id="44"}, | |
{id="45"}, {id="46",sham=true}, {id="47",sham=true}, | |
{id="48",sham=true}, {id="49", sham=true}, {id="50",sham=true}, | |
{id="51",sham=true}, {id="52",sham=true}, {id="53"}, | |
{id="54",sham=true}, {id="55"}, {id="56"}, {id="57"}, | |
{id="58"}, {id="59"}, {id="60"}, {id="61",sham=true}, | |
{id="62"}, {id="63",sham=true}, {id="64",sham=true}, | |
{id="65",sham=true}, {id="66",sham=true}, {id="67"}, | |
{id="68",sham=true}, {id="69"}, {id="70"}, {id="71"}, | |
{id="72",sham=true}, {id="73"}, {id="74"}, {id="75"}, | |
{id="76"}, {id="77"}, {id="78"}, {id="79"}, {id="80"}, | |
{id="81",sham=true}, {id="82"}, {id="83"}, {id="84"}, | |
{id="85"}, {id="86"}, {id="87",sham=true}, {id="88"}, | |
{id="89"}, {id="90",sham=true}, {id="91",sham=true}, | |
{id="92"}, {id="93",sham=true}, {id="94",sham=true}, | |
{id="95",sham=true}, {id="96"}, {id="97",sham=true}, | |
{id="98",sham=true}, {id="99",sham=true}, {id="100"}, | |
{id="101",sham=true}, {id="102",sham=true}, | |
{id="103",sham=true}, {id="104",sham=true}, | |
{id="105",sham=true}, {id="106",sham=true}, | |
{id="107",sham=true}, {id="115",sham=true}, | |
{id="116",sham=true}, {id="117"}, {id="118"}, {id="119"}, | |
{id="120"}, {id="121"}, {id="122"}, {id="123"}, {id="124"}, | |
{id="125"}, {id="126"}, {id="127"}, {id="128",sham=true}, | |
{id="129",sham=true}, {id="130",sham=true}, | |
{id="131",sham=true}, {id="132",sham=true}, | |
{id="133",sham=true}, {id="134",sham=true}, {id="136"}, | |
{id="137",sham=true}, {id="138"}, {id="139",sham=true}, | |
{id="140",sham=true}, {id="141",sham=true}, {id="142"}, | |
{id="143",sham=true}, {id="145"}, | |
{id="146",sham=true}, {id="147",sham=true}, {id="148"}, | |
{id="149"}, {id="150"}, {id="151"} | |
} | |
}, | |
["vanilla% shamless"] = { | |
description = "All vanilla maps that do not need shaman.", | |
sham = false, | |
maps = { | |
{id="1"}, {id="2"}, {id="6"}, {id="7"}, {id="8"}, | |
{id="10"}, {id="11"}, {id="12"}, {id="13"}, {id="14"}, | |
{id="19"}, {id="20"}, {id="22"}, {id="23"}, {id="24"}, | |
{id="26"}, {id="27"}, {id="28"}, {id="30"}, {id="31"}, | |
{id="33"}, {id="35"}, {id="37"}, {id="40"}, {id="41"}, | |
{id="42"}, {id="43"}, {id="44"}, {id="45"}, {id="49"}, | |
{id="53"}, {id="55"}, {id="56"}, {id="57"}, {id="58"}, | |
{id="59"}, {id="60"}, {id="62"}, {id="67"}, {id="69"}, | |
{id="70"}, {id="71"}, {id="73"}, {id="74"}, {id="75"}, | |
{id="76"}, {id="77"}, {id="78"}, {id="79"}, {id="80"}, | |
{id="82"}, {id="83"}, {id="84"}, {id="85"}, {id="86"}, | |
{id="88"}, {id="89"}, {id="92"}, {id="96"}, {id="100"}, | |
{id="114"}, {id="117"}, {id="118"}, {id="119"}, {id="120"}, | |
{id="121"}, {id="122"}, {id="123"}, {id="124"}, {id="125"}, | |
{id="126"}, {id="127"}, {id="136"}, {id="138"}, {id="142"}, | |
{id="145"}, {id="148"}, {id="149"}, {id="150"}, {id="151"} | |
} | |
}, | |
["bootcamp%"] = { | |
description = "Selection of bootcamp maps.", | |
sham = false, | |
maps = {} | |
}, | |
["regular"] = { | |
description = "Selection of regular maps.", | |
sham = false, | |
maps = {} | |
} | |
} | |
player = nil | |
category = nil | |
mapidx = 1 | |
totalTime = 0 | |
numDeaths = 0 | |
gameIsRunning = false | |
respawnTimer = 0 | |
needsRespawn = false | |
wins = 0 | |
skips = 0 | |
defaultMap = "@0" | |
---------------------------------------------------------------------- | |
-- SPEEDRUN COMMANDS | |
---------------------------------------------------------------------- | |
function newGame(playerName, categoryName) | |
if catData[categoryName] == nil then | |
print('<b><font color="#FF0000">ERROR:</font></b> Could not find category with given name.') | |
end | |
ui.removeTextArea(3, nil) | |
ui.addTextArea(2, "00:00:00:000", nil, -110, 5, 100, 20, 0x324650, 0x000000, 1, true) | |
player = playerName | |
category = categoryName | |
mapidx = 1 | |
-- this makes the estimate an upper bound | |
totalTime = 500 | |
numDeaths = 0 | |
skips = 0 | |
wins = 0 | |
gameIsRunning = true | |
respawn() | |
end | |
function gameOver(complete) | |
if skips > 0 then | |
complete = false | |
end | |
if complete then | |
completeMsg = "" | |
else | |
completeMsg = "(incomplete)" | |
end | |
gameIsRunning = false | |
local content = [[ | |
<b>GAME OVER</b> | |
%s | |
Time: %s | |
Deaths: %d | |
Maps completed: %d | |
]] | |
content = string.format(content, completeMsg, getTime(), numDeaths, wins) | |
ui.removeTextArea(2, nil) | |
ui.addTextArea(3, content, nil, 350, 50, 150, 70, 0x324650, 0x000000, 1, true) | |
tfm.exec.newGame(defaultMap) | |
end | |
function respawn() | |
if respawnTimer == 0 and not needsRespawn then | |
resetMap() | |
else | |
needsRespawn = true | |
end | |
end | |
---------------------------------------------------------------------- | |
-- PRACTICE COMMANDS | |
---------------------------------------------------------------------- | |
---------------------------------------------------------------------- | |
-- MAP COMMANDS | |
---------------------------------------------------------------------- | |
function resetMap() | |
respawnTimer = 7 | |
local map = catData[category].maps[mapidx] | |
tfm.exec.newGame(map.id) | |
-- Kill all other players | |
for playerName, playerData in pairs(tfm.get.room.playerList) do | |
if playerName ~= player then | |
tfm.exec.killPlayer(playerName) | |
end | |
end | |
-- Set the shaman if needed | |
local sham = catData[category].sham | |
if map.sham ~= nil then sham = map.sham end | |
if sham then tfm.exec.setShaman(player) end | |
-- Set the game time to be much longer than necessary | |
tfm.exec.setGameTime(1200) | |
end | |
function nextMap() | |
mapidx = mapidx + 1 | |
if mapidx <= #catData[category].maps then | |
respawn() | |
else | |
gameOver(true) | |
end | |
end | |
---------------------------------------------------------------------- | |
-- TIMER COMMANDS | |
---------------------------------------------------------------------- | |
function getTime(tmp) | |
tmp = 0 | |
local time = totalTime + tmp | |
local millis = math.fmod(time, 1000) | |
local seconds = math.fmod(math.floor(time / 1000), 60) | |
local minutes = math.fmod(math.floor(time / 60000), 60) | |
local hours = math.fmod(math.floor(time / 3600000), 60) | |
return string.format("%02d:%02d:%02d:%03d", hours, minutes, seconds, millis) | |
end | |
function updateTimer(tmp) | |
if not gameIsRunning then return end | |
totalTime = totalTime + 500 | |
ui.updateTextArea(2, getTime(tmp), nil) | |
end | |
---------------------------------------------------------------------- | |
-- EVENTS | |
---------------------------------------------------------------------- | |
function eventChatCommand(playerName, message) | |
if gameIsRunning and playerName == player then | |
if message == "reset" then | |
numDeaths = numDeaths + 1 | |
respawn() | |
return | |
elseif message == "stop" then | |
gameOver(false) | |
return | |
elseif message == "skip" then | |
skips = skips + 1 | |
nextMap() | |
return | |
end | |
end | |
if string.sub(message, 0, 4) == "play" then | |
local categoryName = string.sub(message, 6) | |
newGame(playerName, categoryName) | |
elseif message == "categories" then | |
msg = '\n <b><font color="#FF0000">CATEGORIES</font></b>' | |
for categoryName, data in pairs(catData) do | |
msg = msg .. '\n <font color="#4444FF">' .. categoryName .. "</font> " .. data.description | |
end | |
print(msg) | |
elseif message == "help" then | |
msg = [[ | |
commands: | |
<font color="#FF0000">!play</font> <font color="#00FF00">[category]</font> - Play a game category. | |
<font color="#FF0000">!categories</font> - List all categories | |
<font color="#FF0000">!help</font> - Show this dialog | |
in-game commands: (current player only) | |
<font color="#FF0000">!reset</font> - Resets the current map | |
<font color="#FF0000">!skip</font> - Skips the current map, but invalidates the run | |
<font color="#FF0000">!stop</font> - Stops the current run | |
]] | |
print(msg) | |
else | |
print('<b><font color="#FF0000">ERROR:</font></b> Invalid command.') | |
end | |
end | |
function eventPlayerWon(playerName, timeElapsed, timeElapsedSinceRespawn) | |
if not gameIsRunning then return end | |
if playerName == player then | |
wins = wins + 1 | |
nextMap() | |
end | |
end | |
function eventPlayerDied(playerName) | |
if not gameIsRunning then return end | |
if playerName == player then | |
numDeaths = numDeaths + 1 | |
respawn() | |
end | |
end | |
function eventLoop(currentTime, timeRemaining) | |
if not gameIsRunning then return end | |
updateTimer() | |
if respawnTimer > 0 then | |
respawnTimer = respawnTimer - 1 | |
end | |
if respawnTimer == 0 and needsRespawn then | |
needsRespawn = false | |
resetMap() | |
end | |
end | |
---------------------------------------------------------------------- | |
-- MODULE INITIALIZATION | |
---------------------------------------------------------------------- | |
-- Basic game settings | |
tfm.exec.disableAutoNewGame(true) | |
tfm.exec.disableAutoScore(true) | |
tfm.exec.disableAutoTimeLeft(true) | |
tfm.exec.disableAutoShaman(true) | |
tfm.exec.disableAllShamanSkills(true) | |
tfm.exec.disableAfkDeath(true) | |
tfm.exec.setRoomMaxPlayers(1) | |
-- Start in the defult map | |
tfm.exec.newGame(defaultMap) | |
-- Print a welcome message | |
print([[ | |
This module is for speed running Transformice maps. | |
To see the map categories available, enter <font color="#FF0000">!categories</font> | |
To run a particular category, enter <font color="#FF0000">!play</font> <font color="#00FF00">[category]</font> | |
To see a description of all commands, enter <font color="#FF0000">!help</font> | |
]]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment