Fixed fake conflict in ThemeAndGamePrefs.lua.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
-- This will discuss how to create an option row in lua for use on a normal options screen.
|
||||
|
||||
-- To try out this example, copy this file to Scripts/ and add the following
|
||||
-- line to metrics.ini, on one of the options screens. Change the "1" to an
|
||||
-- appropriate line name and make sure it's in the LineNames list for that
|
||||
-- screen.
|
||||
-- Line1="lua,FooMods()"
|
||||
|
||||
-- Make sure you test this with Player 2, Player 1 can't interact with this
|
||||
-- option row as part of the example.
|
||||
-- When on the screen testing the example, flush the log and read it when
|
||||
-- interacting with the option row. This example doesn't actually apply any
|
||||
-- modifiers to the player, it just prints messages to the log file so you can
|
||||
-- see what functions are being called.
|
||||
|
||||
-- Comments explaining an element come before the element they explain.
|
||||
|
||||
-- The function "FooMods" returns a table containing all the information the
|
||||
-- option row handler needs to build the option row.
|
||||
function FooMods()
|
||||
return {
|
||||
-- A string with the name of the row. This name will be localized using
|
||||
-- the entry in the "OptionTitles" section of the language file.
|
||||
Name= "Foo",
|
||||
|
||||
-- A boolean controlling whether the choice affects all players.
|
||||
OneChoiceForAllPlayers= false,
|
||||
|
||||
-- A boolean controlling whether SaveSelections is called after every
|
||||
-- change. If this is true, SaveSelections will be called every time
|
||||
-- the player moves the cursor on the row.
|
||||
ExportOnChange= false,
|
||||
|
||||
-- A LayoutType enum value. "ShowAllInRow" shows all items in the row,
|
||||
-- "ShowOneInRow" shows only the choice with focus is shown.
|
||||
-- "ShowOneInRow" is forced if there are enough choices that they would go
|
||||
-- off screen.
|
||||
LayoutType= "ShowAllInRow",
|
||||
|
||||
-- A SelectType enum value. "SelectOne" allows only one choice to be
|
||||
-- selected. "SelectMultiple" allows multiple to be selected.
|
||||
-- "SelectNone" allows none to be selected.
|
||||
SelectType= "SelectMultiple",
|
||||
|
||||
-- Optional function. If non-nil, this function must return a table of
|
||||
-- PlayerNumbers that are allowed to use the row.
|
||||
-- A row that can't be used by one player can be confusing for the players
|
||||
-- so consider carefully before using this.
|
||||
-- This function will be called an extra time during loading to ensure
|
||||
-- it returns a table.
|
||||
EnabledForPlayers= function(self)
|
||||
Trace("FooMods:EnabledForPlayers() called.")
|
||||
-- Leave out PLAYER_1 just for example.
|
||||
return {PLAYER_2}
|
||||
end,
|
||||
|
||||
-- A table of strings that are the names of choices. Choice names are not
|
||||
-- localized.
|
||||
Choices= {"a", "b", "c", "d"},
|
||||
|
||||
-- Optional table. If non-nil, this table must contain a list of messages
|
||||
-- this row should listen for. If one of the messages is recieved, the
|
||||
-- row is reloaded (and the EnabledForPlayers function is called if it is
|
||||
-- non-nil).
|
||||
ReloadRowMessages= {"ReloadFooMods"},
|
||||
|
||||
-- LoadSelections should examine the player and figure out which options
|
||||
-- on the row the player has selected.
|
||||
-- self is the table returned by the original function used to create the
|
||||
-- option row. (the table being created right now).
|
||||
-- list is a table of bools, all initially false. Set them to true to
|
||||
-- indicate which options are on.
|
||||
-- pn is the PlayerNumber of the player the selections are for.
|
||||
LoadSelections= function(self, list, pn)
|
||||
Trace("FooMods:LoadSelections(" .. pn .. ")")
|
||||
for i, choice in ipairs(self.Choices) do
|
||||
-- Randomly set some to true just for an example.
|
||||
if math.random(0, 1) == 1 then
|
||||
Trace(choice .. " (" .. i .. ")" .. " set to true.")
|
||||
list[i]= true
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
-- SaveSelections should examine the list of what the player has selected
|
||||
-- and apply the appropriate modifiers to the player.
|
||||
-- Same args as LoadSelections.
|
||||
SaveSelections= function(self, list, pn)
|
||||
Trace("FooMods:SaveSelections(" .. pn .. ")")
|
||||
for i, choice in ipairs(self.Choices) do
|
||||
if list[i] then
|
||||
Trace(choice .. " (" .. i .. ")" .. " set to true.")
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
-- Optional function. If non-nil, this function must take 3 parameters
|
||||
-- (self, pn, choice), and return a bool. It is called when a player
|
||||
-- selects an item in the row by pressing start.
|
||||
-- self is the same as for LoadSelections.
|
||||
-- pn is the PlayerNumber of the player that made the selection.
|
||||
-- choice is the choice the player's cursor is on.
|
||||
-- The return value should be true if the Choices table is changed. If it
|
||||
-- is true, then LoadSelections will be called to update which choices are
|
||||
-- underlined for each player.
|
||||
-- This function is meant to provide a way for a menu to change the text of
|
||||
-- its choices. If it returns true, LoadSelections will be called for each
|
||||
-- player. If OneChoiceForAllPlayers is true, this function will be called
|
||||
-- for each player, which means LoadSelections will be called twice for
|
||||
-- each player. Well written code shouldn't have a problem with this.
|
||||
NotifyOfSelection= function(self, pn, choice)
|
||||
Trace("FooMods:NotifyOfSelection(" .. pn .. ", " .. choice .. ")")
|
||||
-- Randomly decide whether to change, as an example.
|
||||
local change= math.random(0, 3)
|
||||
-- No change half the time, lengthen or clip strings the other half.
|
||||
if change < 2 then
|
||||
Trace("Not changing choices.")
|
||||
else
|
||||
Trace("Changing choices.")
|
||||
end
|
||||
if change == 2 then
|
||||
for i, choice in ipairs(self.Choices) do
|
||||
self.Choices[i]= choice .. choice
|
||||
end
|
||||
elseif change == 3 then
|
||||
for i, choice in ipairs(self.Choices) do
|
||||
self.Choices[i]= choice:sub(1, 1)
|
||||
end
|
||||
end
|
||||
-- Don't actually broadcast a reload message from here, do it from some
|
||||
-- other place that actually has a reason to trigger a reload. This is
|
||||
-- just here so that nothing needs to be added to a screen's lua files
|
||||
-- for this example.
|
||||
if math.random(0, 5) == 0 then
|
||||
Trace("Broadcasting reload message.")
|
||||
MESSAGEMAN:Broadcast("ReloadFooMods")
|
||||
end
|
||||
return change >= 2
|
||||
end
|
||||
}
|
||||
end
|
||||
@@ -401,6 +401,7 @@ Hide=Hide
|
||||
Holds=Holds
|
||||
Insert=Insert
|
||||
Insert Credit=Insert Credit
|
||||
Invalid=Invalid row definition.
|
||||
Key Joy Mappings=Map keys and joystick buttons to game functions.
|
||||
Language=Choose your language.
|
||||
LifeDifficulty=Increase this value to cause your life meter to drain faster and refill slower.
|
||||
@@ -478,6 +479,8 @@ SoundResampleQuality=Advanced: Select the resampling quality to use.
|
||||
SoundVolume=Adjust the volume level during gameplay.
|
||||
SoundVolumeAttract=Adjust the volume level during attract mode.
|
||||
Speed=Speed
|
||||
Speed Increment=Size of the smaller increments used to adjust the speed mod.
|
||||
Speed Multiple=Multiplied by Speed Increment to calculate the size of the large increments used to adjust the speed mod.
|
||||
StepMania Credits=The StepMania Development Team.
|
||||
Steps=Steps
|
||||
StretchBackgrounds=Toggle the stretching of backgrounds.
|
||||
@@ -973,6 +976,7 @@ Insert=Insert
|
||||
Insert Credit=Insert Credit
|
||||
Insert beat and shift down=Insert beat and shift down
|
||||
Insert Entry=Insert Entry
|
||||
Invalid=Invalid
|
||||
Invert notes' player=Invert player notes (routine only)
|
||||
Jumps=Jumps
|
||||
Key Joy Mappings=Config Key/Joy Mappings
|
||||
@@ -1099,6 +1103,8 @@ SoundResampleQuality=Resampling Quality
|
||||
SoundVolume=Sound Volume
|
||||
SoundVolumeAttract=Attract Volume
|
||||
Speed=Speed
|
||||
Speed Increment=Speed Increment
|
||||
Speed Multiple=Speed Multiple
|
||||
Step Author=Step Author
|
||||
Steps=Steps
|
||||
Stream=Stream
|
||||
|
||||
@@ -34,6 +34,15 @@ function string:find_last(text)
|
||||
end
|
||||
end
|
||||
|
||||
-- Round to nearest integer.
|
||||
function math.round(n)
|
||||
if n > 0 then
|
||||
return math.floor(n+0.5)
|
||||
else
|
||||
return math.ceil(n-0.5)
|
||||
end
|
||||
end
|
||||
|
||||
-- (c) 2006 Glenn Maynard
|
||||
-- All rights reserved.
|
||||
--
|
||||
|
||||
@@ -91,7 +91,6 @@ function OptionsRandomJukebox()
|
||||
GAMESTATE:SetJukeboxUsesModifiers(val)
|
||||
end
|
||||
}
|
||||
setmetatable( t, t )
|
||||
return t
|
||||
end
|
||||
|
||||
@@ -134,7 +133,6 @@ function OptionsWeight()
|
||||
end
|
||||
end,
|
||||
}
|
||||
setmetatable(t, t)
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
@@ -243,10 +243,223 @@ function SpeedMods()
|
||||
state:SetPlayerOptions("ModsLevel_Preferred", self.Choices[1])
|
||||
end
|
||||
}
|
||||
setmetatable( t, t )
|
||||
return t
|
||||
end
|
||||
|
||||
local default_speed_increment= 25
|
||||
local default_speed_inc_multiple= 4
|
||||
|
||||
local function get_speed_increment()
|
||||
local increment= default_speed_increment
|
||||
if ReadGamePrefFromFile("SpeedIncrement") then
|
||||
increment= tonumber(GetGamePref("SpeedIncrement")) or default_speed_increment
|
||||
else
|
||||
WriteGamePrefToFile("SpeedIncrement", increment)
|
||||
end
|
||||
return increment
|
||||
end
|
||||
|
||||
local function get_speed_multiple()
|
||||
local multiple= default_speed_inc_multiple
|
||||
if ReadGamePrefFromFile("SpeedMultiple") then
|
||||
multiple= tonumber(GetGamePref("SpeedMultiple")) or default_speed_inc_multiple
|
||||
else
|
||||
WriteGamePrefToFile("SpeedMultiple", multiple)
|
||||
end
|
||||
return multiple
|
||||
end
|
||||
|
||||
function SpeedModIncSize()
|
||||
-- An option row for controlling the size of the increment used by
|
||||
-- ArbitrarySpeedMods.
|
||||
local increment= get_speed_increment()
|
||||
local ret= {
|
||||
Name= "Speed Increment",
|
||||
LayoutType= "ShowAllInRow",
|
||||
SelectType= "SelectMultiple",
|
||||
OneChoiceForAllPlayers= true,
|
||||
LoadSelections= function(self, list, pn)
|
||||
-- The first value is the status element, only it should be true.
|
||||
list[1]= true
|
||||
end,
|
||||
SaveSelections= function(self, list, pn)
|
||||
WriteGamePrefToFile("SpeedIncrement", increment)
|
||||
end,
|
||||
NotifyOfSelection= function(self, pn, choice)
|
||||
-- return true even though we didn't actually change anything so that
|
||||
-- the underlines will stay correct.
|
||||
if choice == 1 then return true end
|
||||
local incs= {10, 1, -1, -10}
|
||||
local new_val= increment + incs[choice-1]
|
||||
if new_val > 0 then
|
||||
increment= new_val
|
||||
end
|
||||
self:GenChoices()
|
||||
return true
|
||||
end,
|
||||
GenChoices= function(self)
|
||||
self.Choices= {tostring(increment), "+10", "+1", "-1", "-10"}
|
||||
end
|
||||
}
|
||||
ret:GenChoices()
|
||||
return ret
|
||||
end
|
||||
|
||||
function SpeedModIncMultiple()
|
||||
-- An option row for controlling the size of the increment used by
|
||||
-- ArbitrarySpeedMods.
|
||||
local multiple= get_speed_multiple()
|
||||
local ret= {
|
||||
Name= "Speed Multiple",
|
||||
LayoutType= "ShowAllInRow",
|
||||
SelectType= "SelectMultiple",
|
||||
OneChoiceForAllPlayers= true,
|
||||
LoadSelections= function(self, list, pn)
|
||||
-- The first value is the status element, only it should be true.
|
||||
list[1]= true
|
||||
end,
|
||||
SaveSelections= function(self, list, pn)
|
||||
WriteGamePrefToFile("SpeedMultiple", multiple)
|
||||
end,
|
||||
NotifyOfSelection= function(self, pn, choice)
|
||||
-- return true even though we didn't actually change anything so that
|
||||
-- the underlines will stay correct.
|
||||
if choice == 1 then return true end
|
||||
local incs= {5, 1, -1, -5}
|
||||
local new_val= multiple + incs[choice-1]
|
||||
if new_val > 0 then
|
||||
multiple= new_val
|
||||
end
|
||||
self:GenChoices()
|
||||
return true
|
||||
end,
|
||||
GenChoices= function(self)
|
||||
self.Choices= {tostring(multiple), "+5", "+1", "-1", "-5"}
|
||||
end
|
||||
}
|
||||
ret:GenChoices()
|
||||
return ret
|
||||
end
|
||||
|
||||
function ArbitrarySpeedMods()
|
||||
-- If players are allowed to join while this option row is active, problems will probably occur.
|
||||
local increment= get_speed_increment()
|
||||
local multiple= get_speed_multiple()
|
||||
local ret= {
|
||||
Name= "Speed",
|
||||
LayoutType= "ShowAllInRow",
|
||||
SelectType= "SelectMultiple",
|
||||
OneChoiceForAllPlayers= false,
|
||||
LoadSelections= function(self, list, pn)
|
||||
-- The first values display the current status of the speed mod.
|
||||
if pn == PLAYER_1 or self.NumPlayers == 1 then
|
||||
list[1]= true
|
||||
else
|
||||
list[2]= true
|
||||
end
|
||||
end,
|
||||
SaveSelections= function(self, list, pn)
|
||||
local val= self.CurValues[pn]
|
||||
local poptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Preferred")
|
||||
-- modify stage, song and current too so this will work in edit mode.
|
||||
local stoptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Stage")
|
||||
local soptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Song")
|
||||
local coptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Current")
|
||||
if val.mode == "x" then
|
||||
local speed= val.speed / 100
|
||||
poptions:XMod(speed)
|
||||
stoptions:XMod(speed)
|
||||
soptions:XMod(speed)
|
||||
coptions:XMod(speed)
|
||||
elseif val.mode == "C" then
|
||||
poptions:CMod(val.speed)
|
||||
stoptions:CMod(val.speed)
|
||||
soptions:CMod(val.speed)
|
||||
coptions:CMod(val.speed)
|
||||
else
|
||||
poptions:MMod(val.speed)
|
||||
stoptions:MMod(val.speed)
|
||||
soptions:MMod(val.speed)
|
||||
coptions:MMod(val.speed)
|
||||
end
|
||||
end,
|
||||
NotifyOfSelection= function(self, pn, choice)
|
||||
-- Adjust for the status elements
|
||||
local real_choice= choice - self.NumPlayers
|
||||
-- return true even though we didn't actually change anything so that
|
||||
-- the underlines will stay correct.
|
||||
if real_choice < 1 then return true end
|
||||
local val= self.CurValues[pn]
|
||||
if real_choice < 5 then
|
||||
local big_inc= increment * multiple
|
||||
local incs= {big_inc, increment, -increment, -big_inc}
|
||||
local new_val= val.speed + incs[real_choice]
|
||||
if new_val > 0 then
|
||||
val.speed= math.round(new_val)
|
||||
end
|
||||
elseif real_choice >= 5 then
|
||||
val.mode= ({"x", "C", "m"})[real_choice - 4]
|
||||
end
|
||||
self:GenChoices()
|
||||
return true
|
||||
end,
|
||||
GenChoices= function(self)
|
||||
-- We can't show different options to each player, so compromise by
|
||||
-- only showing the xmod increments if one player is in that mode.
|
||||
local show_x_incs= false
|
||||
for pn, val in pairs(self.CurValues) do
|
||||
if val.mode == "x" then
|
||||
show_x_incs= true
|
||||
end
|
||||
end
|
||||
local big_inc= increment * multiple
|
||||
local small_inc= increment
|
||||
if show_x_incs then
|
||||
big_inc= tostring(big_inc / 100)
|
||||
small_inc= tostring(small_inc / 100)
|
||||
else
|
||||
big_inc= tostring(big_inc)
|
||||
small_inc= tostring(small_inc)
|
||||
end
|
||||
self.Choices= {
|
||||
"+" .. big_inc, "+" .. small_inc, "-" .. small_inc, "-" .. big_inc,
|
||||
"Xmod", "Cmod", "Mmod"}
|
||||
-- Insert the status element for P2 first so it will be second
|
||||
for i, pn in ipairs({PLAYER_2, PLAYER_1}) do
|
||||
local val= self.CurValues[pn]
|
||||
if val then
|
||||
if val.mode == "x" then
|
||||
table.insert(self.Choices, 1, (val.speed/100) .. "x")
|
||||
else
|
||||
table.insert(self.Choices, 1, val.mode .. val.speed)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
CurValues= {}, -- for easy tracking of what speed the player wants
|
||||
NumPlayers= 0 -- for ease when adjusting for the status elements.
|
||||
}
|
||||
for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do
|
||||
local poptions= GAMESTATE:GetPlayerState(pn):GetPlayerOptions("ModsLevel_Preferred")
|
||||
local speed= nil
|
||||
local mode= nil
|
||||
if poptions:MaxScrollBPM() > 0 then
|
||||
mode= "m"
|
||||
speed= math.round(poptions:MaxScrollBPM())
|
||||
elseif poptions:TimeSpacing() > 0 then
|
||||
mode= "C"
|
||||
speed= math.round(poptions:ScrollBPM())
|
||||
else
|
||||
mode= "x"
|
||||
speed= math.round(poptions:ScrollSpeed() * 100)
|
||||
end
|
||||
ret.CurValues[pn]= {mode= mode, speed= speed}
|
||||
ret.NumPlayers= ret.NumPlayers + 1
|
||||
end
|
||||
ret:GenChoices()
|
||||
return ret
|
||||
end
|
||||
|
||||
--[[
|
||||
CustomSpeedMods (c) 2013 StepMania team.
|
||||
|
||||
|
||||
@@ -121,7 +121,6 @@ function OptionRowProTiming()
|
||||
setenv("ProTiming"..pname, val); --]]
|
||||
end;
|
||||
};
|
||||
setmetatable( t, t );
|
||||
return t;
|
||||
end;
|
||||
|
||||
|
||||
@@ -2942,11 +2942,13 @@ Line20="conf,VisualDelaySeconds"
|
||||
Fallback="ScreenOptionsServiceChild"
|
||||
NextScreen="ScreenOptionsService"
|
||||
PrevScreen="ScreenOptionsService"
|
||||
LineNames="3,4,8,11,13,14,15,16,28,29,30,31"
|
||||
LineNames="3,4,8,SI,SM,11,13,14,15,16,28,29,30,31"
|
||||
#LineScore="lua,UserPrefScoringMode()"
|
||||
Line3="conf,TimingWindowScale"
|
||||
Line4="conf,LifeDifficulty"
|
||||
Line8="lua,GamePrefDefaultFail()"
|
||||
LineSI="lua,SpeedModIncSize()"
|
||||
LineSM="lua,SpeedModIncMultiple()"
|
||||
Line11="conf,AllowW1"
|
||||
Line13="conf,HiddenSongs"
|
||||
Line14="conf,EasterEggs"
|
||||
@@ -3077,7 +3079,7 @@ PlayMusic=false
|
||||
TimerSeconds=30
|
||||
#
|
||||
LineNames="1,2,3A,3B,4,5,6,R1,R2,7,8,9,10,11,12,13,14,16,17"
|
||||
Line1="lua,SpeedMods()"
|
||||
Line1="lua,ArbitrarySpeedMods()"
|
||||
# Line1="list,Speed"
|
||||
Line2="list,Accel"
|
||||
Line3A="list,EffectsReceptor"
|
||||
|
||||
@@ -1327,7 +1327,7 @@ LineFlashyCombo="lua,ThemePrefRow('FlashyCombo')"
|
||||
[ScreenOptionsGraphicsSound]
|
||||
|
||||
[ScreenOptionsAdvanced]
|
||||
LineNames="3,4,8,11,13,14,16,28,29,30,RollCombo"
|
||||
LineNames="3,4,8,SI,SM,11,13,14,16,28,29,30,RollCombo"
|
||||
LineRollCombo="lua,ThemePrefRow('ComboOnRolls')"
|
||||
|
||||
[ScreenAppearanceOptions]
|
||||
@@ -2011,7 +2011,7 @@ SmallBanner6OffCommand=
|
||||
|
||||
[ScreenEditOptions]
|
||||
LineNames="1,2,3,4,5,6,R1,R2,7,8,9,10,Attacks,11,12,13,14,15,16,SF"
|
||||
Line1="lua,SpeedMods()"
|
||||
Line1="lua,ArbitrarySpeedMods()"
|
||||
LineSF="lua,OptionRowScreenFilter()"
|
||||
|
||||
[StepsDisplayEdit]
|
||||
|
||||
+14
-1
@@ -2,8 +2,9 @@
|
||||
#include "EnumHelper.h"
|
||||
#include "LuaManager.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
|
||||
int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const char *szType, bool bAllowInvalid )
|
||||
int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const char *szType, bool bAllowInvalid, bool bAllowAnything )
|
||||
{
|
||||
luaL_checkany( L, iPos );
|
||||
|
||||
@@ -61,6 +62,18 @@ int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const
|
||||
LuaHelpers::Pop( L, sGot );
|
||||
}
|
||||
LuaHelpers::Push( L, ssprintf("Expected %s; got %s", szType, sGot.c_str() ) );
|
||||
// There are a couple places where CheckEnum is used outside of a
|
||||
// function called from lua. If we use lua_error from one of them,
|
||||
// StepMania crashes out completely. bAllowAnything allows those places
|
||||
// to avoid crashing over theme mistakes.
|
||||
if(bAllowAnything)
|
||||
{
|
||||
RString errmsg;
|
||||
LuaHelpers::Pop(L, errmsg);
|
||||
LOG->Warn(errmsg.c_str());
|
||||
lua_pop(L, 2);
|
||||
return iInvalid;
|
||||
}
|
||||
lua_error( L );
|
||||
}
|
||||
int iRet = lua_tointeger( L, -1 );
|
||||
|
||||
+5
-3
@@ -20,7 +20,8 @@ int CheckEnum(lua_State *L,
|
||||
int iPos,
|
||||
int iInvalid,
|
||||
const char *szType,
|
||||
bool bAllowInvalid);
|
||||
bool bAllowInvalid,
|
||||
bool bAllowAnything= false);
|
||||
|
||||
template<typename T>
|
||||
struct EnumTraits
|
||||
@@ -36,14 +37,15 @@ template<typename T> LuaReference EnumTraits<T>::EnumToString;
|
||||
namespace Enum
|
||||
{
|
||||
template<typename T>
|
||||
static T Check( lua_State *L, int iPos, bool bAllowInvalid = false )
|
||||
static T Check( lua_State *L, int iPos, bool bAllowInvalid = false, bool bAllowAnything= false )
|
||||
{
|
||||
return (T) CheckEnum(L,
|
||||
EnumTraits<T>::StringToEnum,
|
||||
iPos,
|
||||
EnumTraits<T>::Invalid,
|
||||
EnumTraits<T>::szName,
|
||||
bAllowInvalid);
|
||||
bAllowInvalid,
|
||||
bAllowAnything);
|
||||
}
|
||||
template<typename T>
|
||||
static void Push( lua_State *L, T iVal )
|
||||
|
||||
@@ -213,6 +213,26 @@ inline bool MyLua_checkintboolean( lua_State *L, int iArg )
|
||||
return MyLua_checkboolean( L, iArg );
|
||||
}
|
||||
|
||||
// Checks the table at index to verify that it contains strings.
|
||||
inline bool TableContainsOnlyStrings(lua_State* L, int index)
|
||||
{
|
||||
bool passed= true;
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, index) != 0)
|
||||
{
|
||||
// `key' is at index -2 and `value' at index -1
|
||||
const char *pValue = lua_tostring(L, -1);
|
||||
if(pValue == NULL)
|
||||
{
|
||||
// Was going to print an error to the log with the key that failed,
|
||||
// but didn't want to pull in RageLog. -Kyz
|
||||
passed= false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return passed;
|
||||
}
|
||||
|
||||
#define SArg(n) (luaL_checkstring(L,(n)))
|
||||
#define BIArg(n) (MyLua_checkintboolean(L,(n)))
|
||||
#define IArg(n) (luaL_checkint(L,(n)))
|
||||
|
||||
+30
-5
@@ -148,7 +148,7 @@ void OptionRow::LoadExit()
|
||||
ChoicesChanged( RowType_Exit );
|
||||
}
|
||||
|
||||
void OptionRow::ChoicesChanged( RowType type )
|
||||
void OptionRow::ChoicesChanged( RowType type, bool reset_focus )
|
||||
{
|
||||
ASSERT_M( !m_pHand->m_Def.m_vsChoices.empty(), m_pHand->m_Def.m_sName + " has no choices" );
|
||||
|
||||
@@ -181,9 +181,13 @@ void OptionRow::ChoicesChanged( RowType type )
|
||||
|
||||
InitText( type );
|
||||
|
||||
// When choices change, the old focus position is meaningless; reset it.
|
||||
FOREACH_PlayerNumber( p )
|
||||
SetChoiceInRowWithFocus( p, 0 );
|
||||
// Lua can change the choices now, and when it does, we don't want to change focus.
|
||||
if(reset_focus)
|
||||
{
|
||||
// When choices change, the old focus position is meaningless; reset it.
|
||||
FOREACH_PlayerNumber( p )
|
||||
SetChoiceInRowWithFocus( p, 0 );
|
||||
}
|
||||
|
||||
m_textTitle->SetText( GetRowTitle() );
|
||||
}
|
||||
@@ -705,6 +709,7 @@ void OptionRow::SetOneSelection( PlayerNumber pn, int iChoice )
|
||||
FOREACH( bool, vb, b )
|
||||
*b = false;
|
||||
vb[iChoice] = true;
|
||||
NotifyHandlerOfSelection(pn, iChoice);
|
||||
}
|
||||
|
||||
void OptionRow::SetOneSharedSelection( int iChoice )
|
||||
@@ -788,11 +793,31 @@ OptionRowDefinition &OptionRow::GetRowDef()
|
||||
return m_pHand->m_Def;
|
||||
}
|
||||
|
||||
void OptionRow::SetSelected( PlayerNumber pn, int iChoice, bool b )
|
||||
bool OptionRow::SetSelected( PlayerNumber pn, int iChoice, bool b )
|
||||
{
|
||||
if( m_pHand->m_Def.m_bOneChoiceForAllPlayers )
|
||||
pn = PLAYER_1;
|
||||
m_vbSelected[pn][iChoice] = b;
|
||||
return NotifyHandlerOfSelection(pn, iChoice);
|
||||
}
|
||||
|
||||
bool OptionRow::NotifyHandlerOfSelection(PlayerNumber pn, int choice)
|
||||
{
|
||||
bool changed= m_pHand->NotifyOfSelection(pn, choice);
|
||||
if(changed)
|
||||
{
|
||||
ChoicesChanged(m_RowType, false);
|
||||
vector<PlayerNumber> vpns;
|
||||
FOREACH_HumanPlayer( p )
|
||||
vpns.push_back( p );
|
||||
ImportOptions(vpns);
|
||||
FOREACH_PlayerNumber(p)
|
||||
{
|
||||
PositionUnderlines(p);
|
||||
}
|
||||
UpdateEnabledDisabled();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void OptionRow::SetExitText( RString sExitText )
|
||||
|
||||
+5
-2
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
RString GetRowTitle() const;
|
||||
|
||||
void ChoicesChanged( RowType type );
|
||||
void ChoicesChanged( RowType type, bool reset_focus= true );
|
||||
void PositionUnderlines( PlayerNumber pn );
|
||||
void PositionIcons( PlayerNumber pn );
|
||||
void UpdateText( PlayerNumber pn );
|
||||
@@ -96,7 +96,10 @@ public:
|
||||
void ResetFocusFromSelection( PlayerNumber pn );
|
||||
|
||||
bool GetSelected( PlayerNumber pn, int iChoice ) const;
|
||||
void SetSelected( PlayerNumber pn, int iChoice, bool b );
|
||||
// SetSelected returns true if the choices changed because of setting.
|
||||
bool SetSelected( PlayerNumber pn, int iChoice, bool b );
|
||||
|
||||
bool NotifyHandlerOfSelection(PlayerNumber pn, int choice);
|
||||
|
||||
const OptionRowDefinition &GetRowDef() const;
|
||||
OptionRowDefinition &GetRowDef();
|
||||
|
||||
+222
-72
@@ -809,7 +809,10 @@ public:
|
||||
LuaReference *m_pLuaTable;
|
||||
LuaReference m_EnabledForPlayersFunc;
|
||||
|
||||
OptionRowHandlerLua() { m_pLuaTable = new LuaReference; Init(); }
|
||||
bool m_TableIsSane;
|
||||
|
||||
OptionRowHandlerLua(): m_TableIsSane(false)
|
||||
{ m_pLuaTable = new LuaReference; Init(); }
|
||||
virtual ~OptionRowHandlerLua() { delete m_pLuaTable; }
|
||||
void Init()
|
||||
{
|
||||
@@ -817,8 +820,133 @@ public:
|
||||
m_pLuaTable->Unset();
|
||||
}
|
||||
|
||||
bool SanityCheckTable(lua_State* L, RString& RowName)
|
||||
{
|
||||
if(m_pLuaTable->GetLuaType() != LUA_TTABLE)
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: Result of \"%s\" is not a table.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
m_pLuaTable->PushSelf(L);
|
||||
lua_getfield(L, -1, "Name");
|
||||
const char *pStr = lua_tostring(L, -1);
|
||||
if( pStr == NULL )
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"Name\" entry is not a string.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "LayoutType");
|
||||
pStr = lua_tostring(L, -1);
|
||||
if(pStr == NULL || StringToLayoutType(pStr) == LayoutType_Invalid)
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"LayoutType\" entry is not a string.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "SelectType");
|
||||
pStr = lua_tostring(L, -1);
|
||||
if(pStr == NULL || StringToSelectType(pStr) == SelectType_Invalid)
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"SelectType\" entry is not a string.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "Choices");
|
||||
if(!lua_istable(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"Choices\" is not a table.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
if(!TableContainsOnlyStrings(L, lua_gettop(L)))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"Choices\" table contains a non-string.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "EnabledForPlayers");
|
||||
if(!lua_isnil(L, -1))
|
||||
{
|
||||
if(!lua_isfunction(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"EnabledForPlayers\" is not a function.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
m_pLuaTable->PushSelf( L );
|
||||
lua_call( L, 1, 1 ); // call function with 1 argument and 1 result
|
||||
if(!lua_istable(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"EnabledForPlayers\" did not return a table.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, -2) != 0)
|
||||
{
|
||||
PlayerNumber pn= Enum::Check<PlayerNumber>(L, -1, true, true);
|
||||
if(pn == PlayerNumber_Invalid)
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"EnabledForPlayers\" contains a non-PlayerNumber.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "ReloadRowMessages");
|
||||
if(!lua_isnil(L, -1))
|
||||
{
|
||||
if(!lua_istable(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"ReloadRowMessages\" is not a table.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
if(!TableContainsOnlyStrings(L, lua_gettop(L)))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"ReloadRowMessages\" table contains a non-string.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "LoadSelections");
|
||||
if(!lua_isfunction(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"LoadSelections\" entry is not a function.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "SaveSelections");
|
||||
if(!lua_isfunction(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"SaveSelections\" entry is not a function.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "NotifyOfSelection");
|
||||
if(!lua_isnil(L, -1) && !lua_isfunction(L, -1))
|
||||
{
|
||||
LOG->Warn("LUA_ERROR: \"%s\" \"NotifyOfSelection\" entry is not a function.", RowName.c_str());
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pop(L, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetEnabledForPlayers()
|
||||
{
|
||||
if(!m_TableIsSane)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Lua *L = LUA->Get();
|
||||
|
||||
if( m_EnabledForPlayersFunc.IsNil() )
|
||||
@@ -833,16 +961,13 @@ public:
|
||||
m_pLuaTable->PushSelf( L );
|
||||
|
||||
lua_call( L, 1, 1 ); // call function with 1 argument and 1 result
|
||||
if( !lua_istable(L, -1) )
|
||||
RageException::Throw( "\"EnabledForPlayers\" did not return a table." );
|
||||
|
||||
m_Def.m_vEnabledForPlayers.clear(); // and fill in with supplied PlayerNumbers below
|
||||
|
||||
lua_pushnil( L );
|
||||
while( lua_next(L, -2) != 0 )
|
||||
{
|
||||
// `key' is at index -2 and `value' at index -1
|
||||
PlayerNumber pn = (PlayerNumber)luaL_checkint( L, -1 );
|
||||
PlayerNumber pn = Enum::Check<PlayerNumber>(L, -1);
|
||||
|
||||
m_Def.m_vEnabledForPlayers.insert( pn );
|
||||
|
||||
@@ -867,111 +992,94 @@ public:
|
||||
|
||||
// Run the Lua expression. It should return a table.
|
||||
m_pLuaTable->SetFromExpression( sLuaFunction );
|
||||
m_TableIsSane= SanityCheckTable(L, sLuaFunction);
|
||||
if(!m_TableIsSane)
|
||||
{
|
||||
m_pLuaTable->PushSelf(L);
|
||||
lua_getfield(L, -1, "Name");
|
||||
const char *pStr = lua_tostring( L, -1 );
|
||||
if(pStr == NULL)
|
||||
{
|
||||
m_Def.m_sName = "Invalid";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Def.m_sName = pStr;
|
||||
}
|
||||
lua_pop( L, 1 );
|
||||
// Add a fake choice so that there won't be a crash.
|
||||
// This is so that a themer that makes a mistake doesn't have to
|
||||
// completely restart and can just reload scripts.
|
||||
m_Def.m_vsChoices.push_back("Error in row.");
|
||||
// Set m_selectType to SELECT_MULTIPLE so we won't hit the assert in
|
||||
// VerifySelected.
|
||||
m_Def.m_selectType= SELECT_MULTIPLE;
|
||||
lua_settop(L, 0); // Release has an assert that forces a clear stack.
|
||||
LUA->Release(L);
|
||||
return;
|
||||
}
|
||||
m_pLuaTable->PushSelf(L);
|
||||
|
||||
if( m_pLuaTable->GetLuaType() != LUA_TTABLE )
|
||||
RageException::Throw( "Result of \"%s\" is not a table.", sLuaFunction.c_str() );
|
||||
|
||||
m_pLuaTable->PushSelf( L );
|
||||
|
||||
lua_pushstring( L, "Name" );
|
||||
lua_gettable( L, -2 );
|
||||
lua_getfield(L, -1, "Name");
|
||||
const char *pStr = lua_tostring( L, -1 );
|
||||
if( pStr == NULL )
|
||||
RageException::Throw( "\"%s\" \"Name\" entry is not a string.", sLuaFunction.c_str() );
|
||||
m_Def.m_sName = pStr;
|
||||
lua_pop( L, 1 );
|
||||
|
||||
lua_pushstring( L, "OneChoiceForAllPlayers" );
|
||||
lua_gettable( L, -2 );
|
||||
m_Def.m_bOneChoiceForAllPlayers = !!lua_toboolean( L, -1 );
|
||||
lua_getfield(L, -1, "OneChoiceForAllPlayers");
|
||||
m_Def.m_bOneChoiceForAllPlayers = lua_toboolean( L, -1 );
|
||||
lua_pop( L, 1 );
|
||||
|
||||
lua_pushstring( L, "ExportOnChange" );
|
||||
lua_gettable( L, -2 );
|
||||
m_Def.m_bExportOnChange = !!lua_toboolean( L, -1 );
|
||||
lua_getfield(L, -1, "ExportOnChange");
|
||||
m_Def.m_bExportOnChange = lua_toboolean( L, -1 );
|
||||
lua_pop( L, 1 );
|
||||
|
||||
lua_pushstring( L, "LayoutType" );
|
||||
lua_gettable( L, -2 );
|
||||
// TODO: Change these to use the proper enum strings like everything
|
||||
// else. This will break theme compatibility, so it has to wait until
|
||||
// after SM5. -Kyz
|
||||
lua_getfield(L, -1, "LayoutType");
|
||||
pStr = lua_tostring( L, -1 );
|
||||
if( pStr == NULL )
|
||||
RageException::Throw( "\"%s\" \"LayoutType\" entry is not a string.", sLuaFunction.c_str() );
|
||||
m_Def.m_layoutType = StringToLayoutType( pStr );
|
||||
ASSERT( m_Def.m_layoutType != LayoutType_Invalid );
|
||||
lua_pop( L, 1 );
|
||||
|
||||
lua_pushstring( L, "SelectType" );
|
||||
lua_gettable( L, -2 );
|
||||
lua_getfield(L, -1, "SelectType");
|
||||
pStr = lua_tostring( L, -1 );
|
||||
if( pStr == NULL )
|
||||
RageException::Throw( "\"%s\" \"SelectType\" entry is not a string.", sLuaFunction.c_str() );
|
||||
m_Def.m_selectType = StringToSelectType( pStr );
|
||||
ASSERT( m_Def.m_selectType != SelectType_Invalid );
|
||||
lua_pop( L, 1 );
|
||||
|
||||
// Iterate over the "Choices" table.
|
||||
lua_pushstring( L, "Choices" );
|
||||
lua_gettable( L, -2 );
|
||||
if( !lua_istable( L, -1 ) )
|
||||
RageException::Throw( "\"%s\" \"Choices\" is not a table.", sLuaFunction.c_str() );
|
||||
|
||||
lua_getfield(L, -1, "Choices");
|
||||
lua_pushnil( L );
|
||||
while( lua_next(L, -2) != 0 )
|
||||
{
|
||||
// `key' is at index -2 and `value' at index -1
|
||||
const char *pValue = lua_tostring( L, -1 );
|
||||
if( pValue == NULL )
|
||||
RageException::Throw( "\"%s\" Column entry is not a string.", sLuaFunction.c_str() );
|
||||
// LOG->Trace( "'%s'", pValue);
|
||||
|
||||
//LOG->Trace( "choice: '%s'", pValue);
|
||||
m_Def.m_vsChoices.push_back( pValue );
|
||||
|
||||
lua_pop( L, 1 ); // removes `value'; keeps `key' for next iteration
|
||||
}
|
||||
|
||||
lua_pop( L, 1 ); // pop choices table
|
||||
|
||||
// Set the EnabledForPlayers function.
|
||||
lua_pushstring( L, "EnabledForPlayers" );
|
||||
lua_gettable( L, -2 );
|
||||
if( !lua_isfunction( L, -1 ) && !lua_isnil( L, -1 ) )
|
||||
RageException::Throw( "\"%s\" \"EnabledForPlayers\" is not a table.", sLuaFunction.c_str() );
|
||||
lua_getfield(L, -1, "EnabledForPlayers");
|
||||
m_EnabledForPlayersFunc.SetFromStack( L );
|
||||
SetEnabledForPlayers();
|
||||
|
||||
// Iterate over the "ReloadRowMessages" table.
|
||||
lua_pushstring( L, "ReloadRowMessages" );
|
||||
lua_gettable( L, -2 );
|
||||
lua_getfield(L, -1, "ReloadRowMessages");
|
||||
if( !lua_isnil( L, -1 ) )
|
||||
{
|
||||
if( !lua_istable( L, -1 ) )
|
||||
RageException::Throw( "\"%s\" \"ReloadRowMessages\" is not a table.", sLuaFunction.c_str() );
|
||||
|
||||
lua_pushnil( L );
|
||||
while( lua_next(L, -2) != 0 )
|
||||
{
|
||||
// `key' is at index -2 and `value' at index -1
|
||||
const char *pValue = lua_tostring( L, -1 );
|
||||
if( pValue == NULL )
|
||||
RageException::Throw( "\"%s\" Column entry is not a string.", sLuaFunction.c_str() );
|
||||
LOG->Trace( "Found ReloadRowMessage '%s'", pValue);
|
||||
|
||||
//LOG->Trace( "Found ReloadRowMessage '%s'", pValue);
|
||||
m_vsReloadRowMessages.push_back( pValue );
|
||||
|
||||
lua_pop( L, 1 ); // removes `value'; keeps `key' for next iteration
|
||||
}
|
||||
}
|
||||
lua_pop( L, 1 ); // pop ReloadRowMessages table
|
||||
|
||||
// Look for "ExportOnChange" value.
|
||||
lua_pushstring( L, "ExportOnChange" );
|
||||
lua_gettable( L, -2 );
|
||||
if( !lua_isnil( L, -1 ) )
|
||||
{
|
||||
m_Def.m_bExportOnChange = !!MyLua_checkboolean( L, -1 );
|
||||
}
|
||||
lua_pop( L, 1 ); // pop ExportOnChange value
|
||||
|
||||
lua_pop( L, 1 ); // pop main table
|
||||
ASSERT( lua_gettop(L) == 0 );
|
||||
|
||||
@@ -986,6 +1094,10 @@ public:
|
||||
|
||||
virtual void ImportOption( OptionRow *pRow, const vector<PlayerNumber> &vpns, vector<bool> vbSelectedOut[NUM_PLAYERS] ) const
|
||||
{
|
||||
if(!m_TableIsSane)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Lua *L = LUA->Get();
|
||||
|
||||
ASSERT( lua_gettop(L) == 0 );
|
||||
@@ -1010,10 +1122,7 @@ public:
|
||||
m_pLuaTable->PushSelf( L );
|
||||
ASSERT( lua_istable( L, -1 ) );
|
||||
|
||||
lua_pushstring( L, "LoadSelections" );
|
||||
lua_gettable( L, -2 );
|
||||
if( !lua_isfunction( L, -1 ) )
|
||||
RageException::Throw( "\"%s\" \"LoadSelections\" entry is not a function.", m_Def.m_sName.c_str() );
|
||||
lua_getfield(L, -1, "LoadSelections");
|
||||
|
||||
// Argument 1 (self):
|
||||
m_pLuaTable->PushSelf( L );
|
||||
@@ -1042,6 +1151,10 @@ public:
|
||||
}
|
||||
virtual int ExportOption( const vector<PlayerNumber> &vpns, const vector<bool> vbSelected[NUM_PLAYERS] ) const
|
||||
{
|
||||
if(!m_TableIsSane)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Lua *L = LUA->Get();
|
||||
|
||||
ASSERT( lua_gettop(L) == 0 );
|
||||
@@ -1064,10 +1177,7 @@ public:
|
||||
m_pLuaTable->PushSelf( L );
|
||||
ASSERT( lua_istable( L, -1 ) );
|
||||
|
||||
lua_pushstring( L, "SaveSelections" );
|
||||
lua_gettable( L, -2 );
|
||||
if( !lua_isfunction( L, -1 ) )
|
||||
RageException::Throw( "\"%s\" \"SaveSelections\" entry is not a function.", m_Def.m_sName.c_str() );
|
||||
lua_getfield(L, -1, "SaveSelections");
|
||||
|
||||
// Argument 1 (self):
|
||||
m_pLuaTable->PushSelf( L );
|
||||
@@ -1094,6 +1204,46 @@ public:
|
||||
// XXX: allow specifying the mask
|
||||
return 0;
|
||||
}
|
||||
virtual bool NotifyOfSelection(PlayerNumber pn, int choice)
|
||||
{
|
||||
if(!m_TableIsSane)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lua *L= LUA->Get();
|
||||
m_pLuaTable->PushSelf(L);
|
||||
|
||||
lua_getfield(L, -1, "NotifyOfSelection");
|
||||
bool changed= false;
|
||||
if(lua_isfunction(L, -1))
|
||||
{
|
||||
m_pLuaTable->PushSelf(L);
|
||||
LuaHelpers::Push(L, pn);
|
||||
// Convert choice to a lua index so it matches up with the Choices table.
|
||||
lua_pushinteger(L, choice+1);
|
||||
lua_call(L, 3, 1);
|
||||
if(lua_toboolean(L, -1))
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
changed= true;
|
||||
m_Def.m_vsChoices.clear();
|
||||
// Iterate over the "Choices" table.
|
||||
lua_getfield(L, -1, "Choices");
|
||||
lua_pushnil( L );
|
||||
while( lua_next(L, -2) != 0 )
|
||||
{
|
||||
// `key' is at index -2 and `value' at index -1
|
||||
const char *pValue = lua_tostring( L, -1 );
|
||||
//LOG->Trace( "choice: '%s'", pValue);
|
||||
m_Def.m_vsChoices.push_back( pValue );
|
||||
lua_pop( L, 1 ); // removes `value'; keeps `key' for next iteration
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_settop(L, 0); // Release has an assert that forces a clear stack.
|
||||
LUA->Release(L);
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
|
||||
class OptionRowHandlerConfig : public OptionRowHandler
|
||||
|
||||
@@ -178,6 +178,8 @@ public:
|
||||
virtual int ExportOption( const vector<PlayerNumber> &, const vector<bool> vbSelected[NUM_PLAYERS] ) const { return 0; }
|
||||
virtual void GetIconTextAndGameCommand( int iFirstSelection, RString &sIconTextOut, GameCommand &gcOut ) const;
|
||||
virtual RString GetScreen( int /* iChoice */ ) const { return RString(); }
|
||||
// Exists so that a lua function can act on the selection. Returns true if the choices should be reloaded.
|
||||
virtual bool NotifyOfSelection(PlayerNumber pn, int choice) { return false; }
|
||||
};
|
||||
|
||||
/** @brief Utilities for the OptionRowHandlers. */
|
||||
|
||||
@@ -895,7 +895,11 @@ void ScreenOptions::ProcessMenuStart( const InputEventPlus &input )
|
||||
{
|
||||
int iChoiceInRow = row.GetChoiceInRowWithFocus(pn);
|
||||
bool bSelected = !row.GetSelected( pn, iChoiceInRow );
|
||||
row.SetSelected( pn, iChoiceInRow, bSelected );
|
||||
bool changed= row.SetSelected( pn, iChoiceInRow, bSelected );
|
||||
if(changed)
|
||||
{
|
||||
AfterChangeValueOrRow(pn);
|
||||
}
|
||||
|
||||
if( bSelected )
|
||||
m_SoundToggleOn.Play();
|
||||
|
||||
Reference in New Issue
Block a user