384 lines
11 KiB
Lua
384 lines
11 KiB
Lua
local PlayerIndexes = { PlayerNumber_P1 = 1, PlayerNumber_P2 = 1 }
|
|
-- Reads mods from a file
|
|
-- keywords: Speed, Noteskins, None, Clear
|
|
local function ReadMods()
|
|
local file = THEME:GetCurrentThemeDirectory() .. "mods.txt"
|
|
local mods = File.Read( file )
|
|
if not mods
|
|
or mods == ""
|
|
then
|
|
local buff = ";Set mods here, one per line or various in one separated with commas\r\n;Keywords: 'Speed', 'Noteskins', 'None', 'Clear'\r\nSpeeds\r\nNoteskins"
|
|
File.Write(file,buff)
|
|
return {
|
|
ReadSpeedMods();
|
|
NOTESKIN:GetNoteSkinNames();
|
|
"Clear";
|
|
}
|
|
end
|
|
|
|
mods = string.gsub(mods,"\r","")
|
|
mods = split("\n",mods)
|
|
|
|
local ret = {}
|
|
local clear = false
|
|
|
|
for k,v in ipairs(mods) do
|
|
if string.sub(v,1,1) == ";"
|
|
or v == ""
|
|
then
|
|
--skip
|
|
elseif v == "Speeds" then
|
|
table.insert(ret, ReadSpeedMods())
|
|
elseif v == "Noteskins" then
|
|
table.insert(ret, NOTESKIN:GetNoteSkinNames())
|
|
else
|
|
local ModToAdd = string.gsub(v,"%s","")
|
|
if string.find(ModToAdd, ",") ~= nil then
|
|
ModToAdd = split(",", ModToAdd)
|
|
if ModToAdd[1] ~= "None" then
|
|
table.insert(ModToAdd,1,"None")
|
|
end
|
|
table.insert(ret,ModToAdd)
|
|
else
|
|
if ModToAdd == "Clear" then
|
|
if not clear then table.insert(ret,ModToAdd) end
|
|
clear = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- This will happen...
|
|
if not clear then table.insert(ret,"Clear") end
|
|
|
|
return ret
|
|
end
|
|
|
|
local modslist = ReadMods()
|
|
|
|
-- Displays the mods with actorscrollers
|
|
local function OptionsListDisplay(player)
|
|
local function GetPlayerMods()
|
|
local playerstate = GAMESTATE:GetPlayerState(player)
|
|
local playermods = playerstate:GetPlayerOptionsArray('ModsLevel_Preferred')
|
|
return playermods
|
|
end
|
|
|
|
local make = {}
|
|
-- iterate modslist entries
|
|
for k,v in ipairs(modslist) do
|
|
-- not kinopio
|
|
local RowToAdd
|
|
if type(v) == "table" then
|
|
-- it's a table, make it a scroller
|
|
local choices = {}
|
|
-- build the choices
|
|
for i,j in ipairs(v) do
|
|
choices[i] = LoadFont()..{ Text=j };
|
|
end
|
|
RowToAdd = Def.ActorScroller {
|
|
NumItemsToDraw=3;
|
|
SecondsPerItem=0.075;
|
|
-- init indexes here
|
|
-- I like how InitCommand is not allowed to be played or queued
|
|
-- (HURR TOO MUCH COMMANDS ARG!)
|
|
SetChoicesCommand=function(self)
|
|
local found = false
|
|
--SCREENMAN:SystemMessage(join(",", defmods))
|
|
for idx,Mod in ipairs(GetPlayerMods()) do
|
|
-- In before I forgot what's this: those are the nod entries
|
|
for jdx,Nod in ipairs(v) do
|
|
if Mod == Nod then
|
|
found = true
|
|
self:SetCurrentAndDestinationItem(jdx-1);
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- oops, no defaults, set them
|
|
if not found then
|
|
for idx,Mod in ipairs(v) do
|
|
if Mod == "1x"
|
|
or Mod == "default"
|
|
or Mod == "None"
|
|
then
|
|
self:SetCurrentAndDestinationItem(idx-1);
|
|
break
|
|
else
|
|
self:SetCurrentAndDestinationItem(0);
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
CleanModsMessageCommand=function(self,params)
|
|
if player ~= params.PlayerNumber then return end
|
|
self:playcommand("SetChoices");
|
|
end;
|
|
InitCommand=function(self)
|
|
self:SetLoop(true);
|
|
self:playcommand("SetChoices");
|
|
end;
|
|
TransformFunction=function(self, offset, itemIndex, numItems)
|
|
self:x(offset * 100);
|
|
local focus = math.abs(offset)
|
|
focus = scale(focus,0,1,1,0.75)
|
|
|
|
self:zoom(focus)
|
|
end;
|
|
ChoiceSwapMessageCommand=function(self,params)
|
|
if params.PlayerNumber ~= player then return end
|
|
if params.Row ~= k then return end
|
|
|
|
local index = params.Index
|
|
--SCREENMAN:SystemMessage(ToEnumShortString(player).." row mods index: "..index);
|
|
if params.bHasLooped then
|
|
self:SetCurrentAndDestinationItem(-1)
|
|
end
|
|
|
|
self:SetDestinationItem(index-1)
|
|
end;
|
|
-- actors are wrapped here:
|
|
children = choices;
|
|
};
|
|
elseif type(v) == "string" then
|
|
-- it's not, make single switchable choice
|
|
RowToAdd = Def.ActorFrame {
|
|
LoadFont()..{
|
|
Text=v;
|
|
-- init highlights here
|
|
SetChoicesCommand=function(self)
|
|
local cc = color("#000000")
|
|
for idx,Mod in ipairs(GetPlayerMods()) do
|
|
if Mod == v then
|
|
cc = color("#ffffff")
|
|
break
|
|
end
|
|
end
|
|
if v == "Clear" then
|
|
cc = color("#0000ff")
|
|
end
|
|
self:diffuse(cc);
|
|
end;
|
|
InitCommand=function(self)
|
|
self:playcommand("SetChoices");
|
|
end;
|
|
-- Following commands you'll understand (I've heard that somewhere...)
|
|
CleanModsMessageCommand=function(self,params)
|
|
--if v == "Clear" then return end
|
|
if player ~= params.PlayerNumber then return end
|
|
self:playcommand("SetChoices");
|
|
--self:diffuse(color("#000000"));
|
|
end;
|
|
-- cmd(diffuse,color("#000000"));
|
|
ChoiceToggleMessageCommand=function(self,params)
|
|
if params.PlayerNumber ~= player then return end
|
|
if params.Row ~= k then return end
|
|
|
|
local cc = color("#ffffff")
|
|
if not params.Activated then
|
|
cc = color("#000000")
|
|
end
|
|
|
|
--self:visible(params.Activated)
|
|
-- why the fuck is not coloring itself? oh I think I got it... duh!
|
|
self:diffuse(cc)
|
|
end;
|
|
}
|
|
}
|
|
else
|
|
--table.remove(modslist,k)
|
|
--Def.Actor { };
|
|
end
|
|
make[k] = RowToAdd
|
|
end
|
|
|
|
return Def.ActorFrame {
|
|
Draw.RoundBox(300,320,50,50,color("1,0,1,0.5"));
|
|
Draw.RoundBox(300,50,25,25,color("1,0,0,0.5"));
|
|
Def.ActorScroller {
|
|
Name="OptionsListScroller";
|
|
NumItemsToDraw=5;
|
|
SecondsPerItem=0.075;
|
|
InitCommand=function(self)
|
|
self:SetLoop(true);
|
|
end;
|
|
TransformFunction=function(self, offset, itemIndex, numItems)
|
|
self:y(offset * 50);
|
|
end;
|
|
children = make;
|
|
};
|
|
}
|
|
end
|
|
|
|
-- Internals
|
|
return Def.ActorFrame {
|
|
OptionsListDisplay(PLAYER_1)..{
|
|
Name="OptionsListDisplay"..PLAYER_1;
|
|
Condition=GAMESTATE:IsHumanPlayer(PLAYER_1);
|
|
InitCommand=function(self)
|
|
if #GAMESTATE:GetHumanPlayers() == 1
|
|
and PREFSMAN:GetPreference("Center1Player")
|
|
then
|
|
self:CenterX()
|
|
else
|
|
self:FromLeft(160)
|
|
end
|
|
self:CenterY();
|
|
end;
|
|
};
|
|
OptionsListDisplay(PLAYER_2)..{
|
|
Name="OptionsListDisplay"..PLAYER_2;
|
|
Condition=GAMESTATE:IsHumanPlayer(PLAYER_2);
|
|
InitCommand=function(self)
|
|
if #GAMESTATE:GetHumanPlayers() == 1
|
|
and PREFSMAN:GetPreference("Center1Player")
|
|
then
|
|
self:CenterX()
|
|
else
|
|
self:FromRight(-160)
|
|
end
|
|
self:CenterY();
|
|
end;
|
|
};
|
|
-- Everything here and beyond is pure magic
|
|
CodeMessageCommand=function(self,params)
|
|
-- I'm reusing code from CustomCodeDetector
|
|
local code = params.Name
|
|
local player = params.PlayerNumber
|
|
local movement = "none"
|
|
local modscleared = false
|
|
|
|
if not GAMESTATE:IsHumanPlayer(player) then return end;
|
|
|
|
if code == "Back" then
|
|
--SCREENMAN:SystemMessage(ToEnumShortString(player).." closed the oplist")
|
|
SCREENMAN:GetTopScreen():Cancel();
|
|
elseif code == "Prev" then
|
|
PlayerIndexes[player] = PlayerIndexes[player]-1
|
|
if PlayerIndexes[player] < 1 then
|
|
PlayerIndexes[player] = #modslist
|
|
movement = "first"
|
|
end
|
|
--SCREENMAN:SystemMessage(ToEnumShortString(player).." index: "..PlayerIndexes[player]);
|
|
elseif code == "Next" then
|
|
PlayerIndexes[player] = PlayerIndexes[player]+1
|
|
if PlayerIndexes[player] > #modslist then
|
|
PlayerIndexes[player] = 1
|
|
movement = "last"
|
|
end
|
|
--SCREENMAN:SystemMessage(ToEnumShortString(player).." index: "..PlayerIndexes[player]);
|
|
elseif code == "Toggle" then
|
|
-- changes will be done here
|
|
-- get playerstate and playermods
|
|
local playerstate = GAMESTATE:GetPlayerState(player)
|
|
local playermods = playerstate:GetPlayerOptionsArray('ModsLevel_Preferred')
|
|
|
|
-- let's check in what index we are
|
|
local ModToAdd = modslist[PlayerIndexes[player]]
|
|
--the mod is a table, scroll those
|
|
if type(ModToAdd) == "table" then
|
|
-- iteration, once again, like in the custom code detector I made :x
|
|
local index = 1
|
|
local found = false
|
|
local looped = false
|
|
for i,j in ipairs(playermods) do
|
|
for k,l in ipairs(ModToAdd) do
|
|
if j == l then
|
|
found = true
|
|
index = k+1
|
|
if index > #ModToAdd then
|
|
index = 1
|
|
looped = true
|
|
end
|
|
--replace
|
|
playermods[i] = ModToAdd[index]
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- looks like nothing was found, we can assume those are the default mods
|
|
-- (1x and default noteskin) they are not listed for some reason...
|
|
if not found then
|
|
-- set the first on the list (nono, better list 1x and default)
|
|
for k,v in ipairs(ModToAdd) do
|
|
if v == "1x"
|
|
or v == "default"
|
|
then
|
|
index = k+1
|
|
if index > #ModToAdd then
|
|
index = 1
|
|
looped = true
|
|
end
|
|
table.insert(playermods, ModToAdd[index])
|
|
break
|
|
elseif v == "None" then
|
|
table.insert(playermods, ModToAdd[2])
|
|
index = 2
|
|
end
|
|
end
|
|
end
|
|
-- send message broadcasts to be able to move things on the oplist displays
|
|
MESSAGEMAN:Broadcast("ChoiceSwap",{ Row = PlayerIndexes[player]; Index = index; PlayerNumber = player; bHasLooped = looped })
|
|
elseif ModToAdd == "Clear" then
|
|
local defaultmods = PREFSMAN:GetPreference("DefaultModifiers")
|
|
playermods = split(",", defaultmods)
|
|
modscleared = true
|
|
--MESSAGEMAN:Broadcast("CleanMods",{ PlayerNumber = player });
|
|
else
|
|
-- if type(ModToAdd) == "string" then end
|
|
-- we know it's a string...
|
|
local found = false
|
|
for i,j in ipairs(playermods) do
|
|
if j == ModToAdd then
|
|
-- remove it
|
|
found = true
|
|
table.remove(playermods, i)
|
|
break
|
|
end
|
|
end
|
|
|
|
if not found then
|
|
table.insert(playermods, ModToAdd)
|
|
end
|
|
MESSAGEMAN:Broadcast("ChoiceToggle",{ Row = PlayerIndexes[player]; Activated = not found; PlayerNumber = player })
|
|
end
|
|
-- let's check if noteskins aren't missing (we're avoiding crashes here...)
|
|
local found = false
|
|
-- iterate again uh
|
|
for i,j in ipairs(playermods) do
|
|
-- yes because noteskins are stored in the second slot...
|
|
for k,l in ipairs(modslist[2]) do
|
|
if j == l then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- apparently there aren't any noteskins...
|
|
if not found then
|
|
table.insert(playermods, "default")
|
|
end
|
|
|
|
-- everything is ready to be set
|
|
playermods = join(", ",playermods)
|
|
playerstate:SetPlayerOptions('ModsLevel_Preferred',playermods)
|
|
-- and thats pretty much it :D
|
|
end
|
|
|
|
if modscleared then
|
|
MESSAGEMAN:Broadcast("CleanMods",{ PlayerNumber = player });
|
|
end
|
|
|
|
--this moves the scroller...
|
|
local scroller = self:GetChild("OptionsListDisplay"..player):GetChild("OptionsListScroller")
|
|
if movement ~= "none" then
|
|
local tomove
|
|
if movement == "first" then
|
|
tomove = PlayerIndexes[player]
|
|
elseif movement == "last" then
|
|
tomove = PlayerIndexes[player]-2
|
|
end
|
|
scroller:SetCurrentAndDestinationItem(tomove);
|
|
end
|
|
scroller:SetDestinationItem(PlayerIndexes[player]-1);
|
|
end;
|
|
}; |