sm64coopdx/mods/flood/main.lua
2023-12-05 14:37:48 -05:00

642 lines
No EOL
25 KiB
Lua

-- name: Flood
-- incompatible: gamemode
-- description: Flood v2.4.5\nBy \\#ec7731\\Agent X\\#dcdcdc\\\n\nThis mod adds a flood escape gamemode\nto sm64ex-coop, you must escape the flood and reach the top of the level before everything is flooded.
if unsupported then return end
local ROUND_STATE_INACTIVE = 0
ROUND_STATE_ACTIVE = 1
local ROUND_COOLDOWN = 600
local SPEEDRUN_MODE_OFF = 0
local SPEEDRUN_MODE_PROGRESS = 1
local SPEEDRUN_MODE_RESTART = 2
local TEX_FLOOD_FLAG = get_texture_info("flood_flag")
gGlobalSyncTable.roundState = ROUND_STATE_INACTIVE
gGlobalSyncTable.timer = ROUND_COOLDOWN
gGlobalSyncTable.level = LEVEL_BOB
gGlobalSyncTable.waterLevel = -20000
gGlobalSyncTable.speedMultiplier = 1
local sFlagIconPrevPos = { x = 0, y = 0 }
local globalTimer = 0
local listedSurvivors = false
local speedrunner = 0
-- localize functions to improve performance
local network_player_connected_count,init_single_mario,warp_to_level,play_sound,network_is_server,network_get_player_text_color_string,djui_chat_message_create,disable_time_stop,network_player_set_description,set_mario_action,obj_get_first_with_behavior_id,obj_check_hitbox_overlap,spawn_mist_particles,vec3f_dist,play_race_fanfare,play_music,djui_hud_set_resolution,djui_hud_get_screen_height,djui_hud_get_screen_width,djui_hud_render_rect,djui_hud_set_font,djui_hud_world_pos_to_screen_pos,clampf,math_floor,djui_hud_measure_text,djui_hud_print_text,hud_render_power_meter,hud_get_value,save_file_erase_current_backup_save,save_file_set_flags,save_file_set_using_backup_slot,find_floor_height,spawn_non_sync_object,set_environment_region,vec3f_set,vec3f_copy,math_random,set_ttc_speed_setting,get_level_name,hud_hide,smlua_text_utils_secret_star_replace,smlua_audio_utils_replace_sequence = network_player_connected_count,init_single_mario,warp_to_level,play_sound,network_is_server,network_get_player_text_color_string,djui_chat_message_create,disable_time_stop,network_player_set_description,set_mario_action,obj_get_first_with_behavior_id,obj_check_hitbox_overlap,spawn_mist_particles,vec3f_dist,play_race_fanfare,play_music,djui_hud_set_resolution,djui_hud_get_screen_height,djui_hud_get_screen_width,djui_hud_render_rect,djui_hud_set_font,djui_hud_world_pos_to_screen_pos,clampf,math.floor,djui_hud_measure_text,djui_hud_print_text,hud_render_power_meter,hud_get_value,save_file_erase_current_backup_save,save_file_set_flags,save_file_set_using_backup_slot,find_floor_height,spawn_non_sync_object,set_environment_region,vec3f_set,vec3f_copy,math.random,set_ttc_speed_setting,get_level_name,hud_hide,smlua_text_utils_secret_star_replace,smlua_audio_utils_replace_sequence
function speedrun_mode(mode)
if mode == nil then
return speedrunner > 0 and network_player_connected_count() == 1
else
return speedrunner == mode and network_player_connected_count() == 1
end
end
-- runs serverside
local function round_start()
gGlobalSyncTable.roundState = ROUND_STATE_ACTIVE
gGlobalSyncTable.timer = if_then_else(gGlobalSyncTable.level == LEVEL_CTT or (gGlobalSyncTable.level == LEVEL_RR and game == GAME_STAR_ROAD), 730, 100)
end
-- runs serverside
local function round_end()
gGlobalSyncTable.roundState = ROUND_STATE_INACTIVE
gGlobalSyncTable.timer = ROUND_COOLDOWN
gGlobalSyncTable.waterLevel = -20000
end
local function get_dest_act()
if game ~= GAME_STAR_ROAD then
return if_then_else(gNetworkPlayers[0].currLevelNum == LEVEL_CASTLE_GROUNDS, 99, 6)
else
if gNetworkPlayers[0].currLevelNum == LEVEL_CASTLE_GROUNDS then
return 99
end
return if_then_else(gNetworkPlayers[0].currLevelNum == LEVEL_BBH, 1, 6)
end
end
local function get_modifiers_string()
if not cheats and not moveset then return "" end
local modifiers = " ("
if moveset then
modifiers = modifiers .. "Moveset"
else
modifiers = modifiers .. "No moveset"
end
if cheats then
modifiers = modifiers .. ", cheats"
end
modifiers = modifiers .. ")"
return modifiers
end
function level_restart()
round_start()
init_single_mario(gMarioStates[0])
mario_set_full_health(gMarioStates[0])
gLevels[gGlobalSyncTable.level].time = 0
warp_to_level(gGlobalSyncTable.level, gLevels[gGlobalSyncTable.level].area, get_dest_act())
end
local function server_update()
if gGlobalSyncTable.roundState == ROUND_STATE_ACTIVE then
if gNetworkPlayers[0].currLevelNum == gGlobalSyncTable.level then
gGlobalSyncTable.waterLevel = gGlobalSyncTable.waterLevel + gLevels[gGlobalSyncTable.level].speed * gGlobalSyncTable.speedMultiplier
local active = 0
for i = 0, (MAX_PLAYERS - 1) do
local m = gMarioStates[i]
if active_player(m) ~= 0 and m.health > 0xFF and not gPlayerSyncTable[i].finished then
active = active + 1
end
end
if active == 0 then
local dead = 0
for i = 0, (MAX_PLAYERS) - 1 do
if active_player(gMarioStates[i]) ~= 0 and gMarioStates[i].health <= 0xFF then
dead = dead + 1
end
end
if dead == network_player_connected_count() or (speedrun_mode() and gNetworkPlayers[0].currLevelNum ~= LEVEL_CTT) then
gGlobalSyncTable.timer = 0
end
if gGlobalSyncTable.timer > 0 then
gGlobalSyncTable.timer = gGlobalSyncTable.timer - 1
else
round_end()
if not speedrun_mode() or speedrun_mode(SPEEDRUN_MODE_PROGRESS) then
-- move to the next level
local finished = 0
for i = 0, (MAX_PLAYERS - 1) do
if active_player(gMarioStates[i]) ~= 0 and gPlayerSyncTable[i].finished then
finished = finished + 1
end
end
if finished ~= 0 then
-- calculate position
local position = 1
for k, v in pairs(gMapRotation) do
if gGlobalSyncTable.level == v then
position = k
end
end
position = position + 1
if position > FLOOD_LEVEL_COUNT - FLOOD_BONUS_LEVELS then
position = 1
end
gGlobalSyncTable.level = gMapRotation[position]
end
end
end
end
end
else
if network_player_connected_count() > 1 then
if gGlobalSyncTable.timer > 0 then
gGlobalSyncTable.timer = gGlobalSyncTable.timer - 1
if gGlobalSyncTable.timer == 30 or gGlobalSyncTable.timer == 60 or gGlobalSyncTable.timer == 90 then
play_sound(SOUND_MENU_CHANGE_SELECT, gMarioStates[0].marioObj.header.gfx.cameraToObject)
elseif gGlobalSyncTable.timer == 11 then
play_sound(SOUND_GENERAL_RACE_GUN_SHOT, gMarioStates[0].marioObj.header.gfx.cameraToObject)
end
else
round_start()
end
end
end
end
local function update()
if network_is_server() then server_update() end
gServerSettings.playerInteractions = PLAYER_INTERACTIONS_NONE
if gGlobalSyncTable.roundState == ROUND_STATE_INACTIVE then
if gNetworkPlayers[0].currLevelNum ~= LEVEL_LOBBY or gNetworkPlayers[0].currActNum ~= 0 then
if speedrun_mode() then
level_restart()
end
warp_to_level(LEVEL_LOBBY, 1, 0)
if not listedSurvivors and globalTimer > 5 then
listedSurvivors = true
local finished = 0
local string = "Survivors:"
for i = 0, (MAX_PLAYERS - 1) do
if gNetworkPlayers[i].connected and gPlayerSyncTable[i].finished then
string = string .. "\n" .. network_get_player_text_color_string(i) .. gNetworkPlayers[i].name
finished = finished + 1
end
end
if finished == 0 then
string = string .. "\n\\#ff0000\\None"
end
djui_chat_message_create(string)
end
end
elseif gGlobalSyncTable.roundState == ROUND_STATE_ACTIVE then
local act = get_dest_act()
if gNetworkPlayers[0].currLevelNum ~= gGlobalSyncTable.level or gNetworkPlayers[0].currActNum ~= act then
listedSurvivors = false
mario_set_full_health(gMarioStates[0])
gLevels[gGlobalSyncTable.level].time = 0
gPlayerSyncTable[0].finished = false
warp_to_level(gGlobalSyncTable.level, gLevels[gGlobalSyncTable.level].area, act)
end
end
-- stops the star spawn cutscenes from happening
local m = gMarioStates[0]
if m.area ~= nil and m.area.camera ~= nil and (m.area.camera.cutscene == CUTSCENE_STAR_SPAWN or m.area.camera.cutscene == CUTSCENE_RED_COIN_STAR_SPAWN) then
m.area.camera.cutscene = 0
m.freeze = 0
disable_time_stop()
end
globalTimer = globalTimer + 1
end
--- @param m MarioState
local function mario_update(m)
if not gNetworkPlayers[m.playerIndex].connected then return end
if m.health > 0xFF then
network_player_set_description(gNetworkPlayers[m.playerIndex], "Alive", 75, 255, 75, 255)
else
network_player_set_description(gNetworkPlayers[m.playerIndex], "Dead", 255, 75, 75, 255)
end
if m.playerIndex ~= 0 then return end
-- action specific modifications
if m.action == ACT_STEEP_JUMP then
m.action = ACT_JUMP
elseif m.action == ACT_JUMBO_STAR_CUTSCENE then
m.flags = m.flags | MARIO_WING_CAP
end
-- disable instant warps
if m.floor ~= nil and (m.floor.type == SURFACE_WARP or (m.floor.type >= SURFACE_PAINTING_WARP_D3 and m.floor.type <= SURFACE_PAINTING_WARP_FC) or (m.floor.type >= SURFACE_INSTANT_WARP_1B and m.floor.type <= SURFACE_INSTANT_WARP_1E)) then
m.floor.type = SURFACE_DEFAULT
end
-- disable insta kills
if m.floor ~= nil and (m.floor.type == SURFACE_INSTANT_QUICKSAND or m.floor.type == SURFACE_INSTANT_MOVING_QUICKSAND) then
m.floor.type = SURFACE_BURNING
end
-- disable damage in lobby
if gGlobalSyncTable.roundState == ROUND_STATE_INACTIVE then
mario_set_full_health(m)
m.peakHeight = m.pos.y
return
end
-- dialog boxes
if (m.action == ACT_SPAWN_NO_SPIN_AIRBORNE or m.action == ACT_SPAWN_NO_SPIN_LANDING or m.action == ACT_SPAWN_SPIN_AIRBORNE or m.action == ACT_SPAWN_SPIN_LANDING) and m.pos.y < m.floorHeight + 10 then
set_mario_action(m, ACT_FREEFALL, 0)
end
-- manage CTT
if gNetworkPlayers[0].currLevelNum == LEVEL_CTT then
m.peakHeight = m.pos.y
local star = obj_get_first_with_behavior_id(id_bhvFinalStar)
if star ~= nil and obj_check_hitbox_overlap(m.marioObj, star) and m.action ~= ACT_JUMBO_STAR_CUTSCENE then
spawn_mist_particles()
set_mario_action(m, ACT_JUMBO_STAR_CUTSCENE, 0)
end
if m.action == ACT_JUMBO_STAR_CUTSCENE and m.actionTimer >= 499 then
set_mario_spectator(m)
end
end
-- check if the player has reached the end of the level
if gNetworkPlayers[0].currLevelNum == gGlobalSyncTable.level and not gPlayerSyncTable[0].finished and ((gNetworkPlayers[0].currLevelNum ~= LEVEL_CTT and m.pos.y == m.floorHeight)
or (gNetworkPlayers[0].currLevelNum == LEVEL_CTT and m.action == ACT_JUMBO_STAR_CUTSCENE) or (m.action & ACT_FLAG_ON_POLE) ~= 0)
and vec3f_dist(m.pos, gLevels[gGlobalSyncTable.level].goalPos) < 600 then
gPlayerSyncTable[0].finished = true
local string = ""
if gNetworkPlayers[0].currLevelNum ~= LEVEL_CTT and not (game == GAME_STAR_ROAD and gNetworkPlayers[0].currLevelNum == LEVEL_RR) then
string = string .. "\\#00ff00\\You escaped the flood!\n"
play_race_fanfare()
else
string = string .. "\\#00ff00\\You escaped the \\#ffff00\\final\\#00ff00\\ flood! Congratulations!\n"
play_music(0, SEQUENCE_ARGS(8, SEQ_EVENT_CUTSCENE_VICTORY), 0)
end
string = string .. "\\#ffffff\\Time: " .. string.format("%.3f", gLevels[gGlobalSyncTable.level].time / 30) .. get_modifiers_string()
djui_chat_message_create(string)
end
-- update spectator if finished, manage other things if not
if gPlayerSyncTable[0].finished then
mario_set_full_health(m)
if network_player_connected_count() > 1 and m.action ~= ACT_JUMBO_STAR_CUTSCENE then
set_mario_spectator(m)
end
else
if m.pos.y + 40 < gGlobalSyncTable.waterLevel then
m.health = m.health - 30
end
if m.action == ACT_QUICKSAND_DEATH then
m.health = 0xFF
end
if m.health <= 0xFF then
if network_player_connected_count() > 1 then
m.area.camera.cutscene = 0
set_mario_spectator(m)
end
else
gLevels[gGlobalSyncTable.level].time = gLevels[gGlobalSyncTable.level].time + 1
end
end
end
local function on_hud_render()
local water = obj_get_first_with_behavior_id(id_bhvWater)
if gNetworkPlayers[0].currLevelNum == gGlobalSyncTable.level and water ~= nil then
djui_hud_set_resolution(RESOLUTION_DJUI)
if gLakituState.pos.y < gGlobalSyncTable.waterLevel - 10 then
switch(water.oAnimState, {
[FLOOD_WATER] = function()
djui_hud_set_adjusted_color(0, 20, 200, 120)
end,
[FLOOD_LAVA] = function()
djui_hud_set_adjusted_color(200, 0, 0, 220)
end,
[FLOOD_SAND] = function()
djui_hud_set_adjusted_color(254, 193, 121, 220)
end,
[FLOOD_MUD] = function()
djui_hud_set_adjusted_color(74, 123, 0, 220)
end
})
djui_hud_render_rect(0, 0, djui_hud_get_screen_width(), djui_hud_get_screen_height())
end
end
djui_hud_set_resolution(RESOLUTION_N64)
djui_hud_set_font(FONT_TINY)
local level = gLevels[gNetworkPlayers[0].currLevelNum]
if level ~= nil and level.name ~= "ctt" then
local out = { x = 0, y = 0, z = 0 }
djui_hud_world_pos_to_screen_pos(level.goalPos, out)
local dX = clampf(out.x - 5, 0, djui_hud_get_screen_width() - 19.2)
local dY = clampf(out.y - 20, 0, djui_hud_get_screen_height() - 19.2)
djui_hud_set_adjusted_color(255, 255, 255, 200)
djui_hud_render_texture_interpolated(TEX_FLOOD_FLAG, sFlagIconPrevPos.x, sFlagIconPrevPos.y, 0.15, 0.15, dX, dY, 0.15, 0.15)
sFlagIconPrevPos.x = dX
sFlagIconPrevPos.y = dY
end
local text = if_then_else(gGlobalSyncTable.roundState == ROUND_STATE_INACTIVE, "Type '/flood start' to start a round", "0.000 seconds" .. get_modifiers_string())
if gNetworkPlayers[0].currAreaSyncValid then
if gGlobalSyncTable.roundState == ROUND_STATE_INACTIVE then
text = if_then_else(network_player_connected_count() > 1, "Round starts in " .. tostring(math_floor(gGlobalSyncTable.timer / 30)), "Type '/flood start' to start a round")
elseif gNetworkPlayers[0].currLevelNum == gGlobalSyncTable.level then
text = tostring(string.format("%.3f", gLevels[gGlobalSyncTable.level].time / 30)) .. " seconds" .. get_modifiers_string()
end
end
local scale = 1
local width = djui_hud_measure_text(text) * scale
local x = (djui_hud_get_screen_width() - width) * 0.5
djui_hud_set_adjusted_color(0, 0, 0, 128)
djui_hud_render_rect(x - 6, 0, width + 12, 16)
djui_hud_set_adjusted_color(255, 255, 255, 255)
djui_hud_print_text(text, x, 0, scale)
hud_render_power_meter(gMarioStates[0].health, djui_hud_get_screen_width() - 64, 0, 64, 64)
djui_hud_set_font(FONT_HUD)
djui_hud_render_texture(gTextures.coin, 5, 5, 1, 1)
djui_hud_print_text(">", 21, 5, 1)
djui_hud_print_text(tostring(hud_get_value(HUD_DISPLAY_COINS)), 37, 5, 1)
if gGlobalSyncTable.speedMultiplier ~= 1 then
djui_hud_print_text(string.format("%.2fx", gGlobalSyncTable.speedMultiplier), 5, 24, 1)
end
end
local function on_level_init()
-- reset save
save_file_erase_current_backup_save()
if gNetworkPlayers[0].currLevelNum ~= LEVEL_CASTLE_GROUNDS then
save_file_set_flags(SAVE_FLAG_HAVE_VANISH_CAP)
save_file_set_flags(SAVE_FLAG_HAVE_WING_CAP)
end
save_file_set_using_backup_slot(true)
if gGlobalSyncTable.roundState == ROUND_STATE_ACTIVE then
if network_is_server() then
local start = gLevels[gGlobalSyncTable.level].customStartPos
if start ~= nil then
gGlobalSyncTable.waterLevel = find_floor_height(start.x, start.y, start.z) - 1200
else
-- only sub areas have a weird issue where this function appears to always return the floor lower limit on level init
gGlobalSyncTable.waterLevel = if_then_else(gLevels[gGlobalSyncTable.level].area == 1, find_floor_height(gMarioStates[0].pos.x, gMarioStates[0].pos.y, gMarioStates[0].pos.z), gMarioStates[0].pos.y) - 1200
end
end
if game == GAME_VANILLA then
if gNetworkPlayers[0].currLevelNum == LEVEL_BITS then
spawn_non_sync_object(
id_bhvCustomStaticObject,
E_MODEL_CTT,
10000, -2000, -40000,
function(o) obj_scale(o, 0.5) end
)
elseif gNetworkPlayers[0].currLevelNum == LEVEL_WDW then
set_environment_region(1, -20000)
end
end
spawn_non_sync_object(
id_bhvWater,
E_MODEL_FLOOD,
0, gGlobalSyncTable.waterLevel, 0,
nil
)
end
local pos = gLevels[gNetworkPlayers[0].currLevelNum].goalPos
if pos == nil then return end
if gNetworkPlayers[0].currLevelNum == LEVEL_CTT then
spawn_non_sync_object(
id_bhvFinalStar,
E_MODEL_STAR,
pos.x, pos.y, pos.z,
nil
)
else
spawn_non_sync_object(
id_bhvFloodFlag,
E_MODEL_KOOPA_FLAG,
pos.x, pos.y, pos.z,
--- @param o Object
function(o)
o.oFaceAnglePitch = 0
o.oFaceAngleYaw = pos.a
o.oFaceAngleRoll = 0
end
)
end
end
-- dynos warps mario back to castle grounds facing the wrong way, likely something from the title screen
local function on_warp()
--- @type MarioState
local m = gMarioStates[0]
if gNetworkPlayers[0].currLevelNum == LEVEL_CASTLE_GROUNDS then
if game == GAME_VANILLA then
m.faceAngle.y = m.faceAngle.y + 0x8000
elseif game == GAME_STAR_ROAD then
if gGlobalSyncTable.roundState == ROUND_STATE_INACTIVE then
vec3f_set(m.pos, -6797, 1830, 2710)
m.faceAngle.y = 0x6000
else
vec3f_set(m.pos, -1644, -614, -1524)
m.faceAngle.y = -0x4000
end
end
if gGlobalSyncTable.roundState == ROUND_STATE_ACTIVE then
play_music(0, SEQUENCE_ARGS(4, SEQ_LEVEL_BOSS_KOOPA_FINAL), 0)
end
elseif gLevels[gGlobalSyncTable.level].customStartPos ~= nil then
local start = gLevels[gGlobalSyncTable.level].customStartPos
vec3f_copy(m.pos, start)
set_mario_action(m, ACT_SPAWN_SPIN_AIRBORNE, 0)
m.faceAngle.y = start.a
end
end
local function on_player_connected()
if network_is_server() and gGlobalSyncTable.roundState == ROUND_STATE_INACTIVE then gGlobalSyncTable.timer = ROUND_COOLDOWN end
end
local function on_start_command(msg)
if msg == "?" then
djui_chat_message_create("/flood \\#00ffff\\start\\#ffff00\\ [random|1-" .. FLOOD_LEVEL_COUNT .. "]\\#ffffff\\\nSets the level to a random one or a specific one, you can also leave it empty for normal progression.")
return true
end
if msg == "random" then
gGlobalSyncTable.level = gLevels[math_random(1, FLOOD_LEVEL_COUNT)]
else
local override = tonumber(msg)
if override ~= nil then
override = clamp(math_floor(override), 1, FLOOD_LEVEL_COUNT)
gGlobalSyncTable.level = gMapRotation[override]
else
for k, v in pairs(gLevels) do
if msg ~= nil and msg:lower() == v.name then
gGlobalSyncTable.level = k
end
end
end
end
if gGlobalSyncTable.roundState == ROUND_STATE_ACTIVE then
network_send(true, { restart = true })
level_restart()
else
round_start()
end
return true
end
local function on_speed_command(msg)
local speed = tonumber(msg)
if speed ~= nil then
speed = clampf(speed, 0, 10)
djui_chat_message_create("Water speed set to " .. speed)
gGlobalSyncTable.speedMultiplier = speed
return true
end
djui_chat_message_create("/flood \\#00ffff\\speed\\#ffff00\\ [number]\\#ffffff\\\nSets the speed multiplier of the flood")
return true
end
local function on_ttc_speed_command(msg)
if gGlobalSyncTable.roundState ~= ROUND_STATE_INACTIVE then
djui_chat_message_create("\\#ff0000\\You can only change the TTC speed before the round starts!")
return true
end
msg = msg:lower()
if msg == "fast" then
set_ttc_speed_setting(TTC_SPEED_FAST)
djui_chat_message_create("TTC speed set to fast")
return true
elseif msg == "slow" then
set_ttc_speed_setting(TTC_SPEED_SLOW)
djui_chat_message_create("TTC speed set to slow")
return true
elseif msg == "random" then
set_ttc_speed_setting(TTC_SPEED_RANDOM)
djui_chat_message_create("TTC speed set to random")
return true
elseif msg == "stopped" then
set_ttc_speed_setting(TTC_SPEED_STOPPED)
djui_chat_message_create("TTC speed stopped")
return true
end
djui_chat_message_create("/flood \\#00ffff\\ttc-speed\\#ffff00\\ [fast|slow|random|stopped]\\#ffffff\\\nChanges the speed of TTC")
return true
end
local function on_speedrun_command(msg)
msg = msg:lower()
if msg == "off" then
djui_chat_message_create("Speedrun mode status: \\#ff0000\\OFF")
speedrunner = SPEEDRUN_MODE_OFF
return true
elseif msg == "progress" then
djui_chat_message_create("Speedrun mode status: \\#00ff00\\Progress Level")
speedrunner = SPEEDRUN_MODE_PROGRESS
return true
elseif msg == "restart" then
djui_chat_message_create("Speedrun mode status: \\#00ff00\\Restart Level")
speedrunner = SPEEDRUN_MODE_RESTART
return true
end
djui_chat_message_create("/flood \\#00ffff\\speedrun\\#ffff00\\ [off|progress|restart]\\#ffffff\\\nTo make adjustments to singleplayer Flood helpful for speedrunners")
return true
end
local function on_scoreboard_command()
djui_chat_message_create("Times:")
local modifiers = get_modifiers_string()
local total = 0
for i = 1, FLOOD_LEVEL_COUNT do
local level = gMapRotation[i]
djui_chat_message_create(get_level_name(level_to_course(level), level, 1) .. " - " .. timestamp(gLevels[level].time) .. modifiers)
total = total + gLevels[level].time
end
djui_chat_message_create("Total Time: " .. timestamp(total))
return true
end
local function on_flood_command(msg)
local args = split(msg)
if args[1] == "start" then
return on_start_command(args[2])
elseif args[1] == "speed" then
return on_speed_command(args[2])
elseif args[1] == "ttc-speed" then
return on_ttc_speed_command(args[2])
elseif args[1] == "speedrun" then
return on_speedrun_command(args[2])
elseif args[1] == "scoreboard" then
return on_scoreboard_command()
end
djui_chat_message_create("/flood \\#00ffff\\[start|speed|ttc-speed|speedrun|scoreboard]")
return true
end
gServerSettings.skipIntro = 1
gServerSettings.stayInLevelAfterStar = 2
gLevelValues.entryLevel = LEVEL_LOBBY
gLevelValues.floorLowerLimit = -20000
gLevelValues.floorLowerLimitMisc = -20000 + 1000
gLevelValues.floorLowerLimitShadow = -20000 + 1000.0
gLevelValues.fixCollisionBugs = 1
gLevelValues.fixCollisionBugsRoundedCorners = 0
hud_hide()
if game == GAME_VANILLA then
set_ttc_speed_setting(TTC_SPEED_SLOW)
smlua_text_utils_secret_star_replace(COURSE_SA, " Climb The Tower Flood")
smlua_audio_utils_replace_sequence(SEQ_LEVEL_BOSS_KOOPA_FINAL, 37, 60, "00_pinball_custom")
end
hook_event(HOOK_UPDATE, update)
hook_event(HOOK_MARIO_UPDATE, mario_update)
hook_event(HOOK_ON_HUD_RENDER, on_hud_render)
hook_event(HOOK_ON_LEVEL_INIT, on_level_init)
hook_event(HOOK_ON_WARP, on_warp)
hook_event(HOOK_ON_PLAYER_CONNECTED, on_player_connected)
if network_is_server() then
hook_chat_command("flood", "\\#00ffff\\[start|speed|ttc-speed|speedrun|scoreboard]", on_flood_command)
end
for i = 0, (MAX_PLAYERS - 1) do
gPlayerSyncTable[i].finished = false
end