-- sm-ssc fallback theme | script ring 03 | Gameplay.lua -- [en] This file is used to store settings that should be different in each -- game mode. -- shakesoda calls this pump.lua local function CurGameName() return GAMESTATE:GetCurrentGame():GetName() end -- Check the active game mode against a string. Cut down typing this in metrics. function IsGame(str) return CurGameName():lower() == str:lower() end -- GetExtraColorThreshold() -- [en] returns the difficulty threshold in meter -- for songs that should be counted as boss songs. function GetExtraColorThreshold() local Modes = { dance = 10, pump = 21, beat = 12, kb7 = 10, para = 10, techno = 10, lights = 10, -- lights shouldn't be playable kickbox= 100, -- extra color is lame } return Modes[CurGameName()] or 10 end -- GameplayMargins exists to provide a layer of backwards compatibility for -- people using the X position metrics to set where the notefields are. -- This makes it somewhat complex. -- Rather than trying to understand how it works, you can simply do this: -- (example values in parentheses) -- 1. Decide how much space you want in the center between notefields. (80) -- 2. Decide how much space you want on each side. (40) -- 3. Write a simple function that just returns those numbers: -- function GameplayMargins() return 40, 80, 40 end -- Then the engine does the work of figuring out where each notefield should -- be centered. function GameplayMargins(enabled_players, styletype) local other= {[PLAYER_1]= PLAYER_2, [PLAYER_2]= PLAYER_1} local margins= {[PLAYER_1]= {40, 40}, [PLAYER_2]= {40, 40}} -- Use a fake style width because calculating the real style width throws off -- the code in the engine. local fake_style_width= 272 -- Handle the case of a single player that is centered first because it's -- simpler. if Center1Player() then local pn= enabled_players[1] fake_style_width= 544 local center= _screen.cx local left= center - (fake_style_width / 2) local right= _screen.w - center - (fake_style_width / 2) -- center margin width will be ignored. return left, 80, right end local half_screen= _screen.w / 2 local left= {[PLAYER_1]= 0, [PLAYER_2]= half_screen} for i, pn in ipairs(enabled_players) do local edge= left[pn] local center= THEME:GetMetric("ScreenGameplay", "Player"..ToEnumShortString(pn)..ToEnumShortString(styletype).."X") -- Adjust for the p2 center being on the right side. center= center - edge margins[pn][1]= center - (fake_style_width / 2) margins[pn][2]= half_screen - center - (fake_style_width / 2) if #enabled_players == 1 then margins[other[pn]][1]= margins[pn][2] margins[other[pn]][2]= margins[pn][1] end end local left= margins[PLAYER_1][1] local center= margins[PLAYER_1][2] + margins[PLAYER_2][1] local right= margins[PLAYER_2][2] return left, center, right end -- AllowOptionsMenu() -- [en] returns if you are able to select options -- on ScreenSelectMusic. function AllowOptionsMenu() if GAMESTATE:IsAnExtraStage() then return false elseif GAMESTATE:GetPlayMode() == "PlayMode_Oni" then return false else return true end end -- GameCompatibleModes: -- [en] returns possible modes for ScreenSelectPlayMode function GameCompatibleModes() local Modes = { dance = "Single,Double,Solo,Versus,Couple", pump = "Single,Double,HalfDouble,Versus,Couple,Routine", beat = "5Keys,7Keys,10Keys,14Keys,Versus5,Versus7", kb7 = "KB7", para = "Single", maniax = "Single,Double,Versus", -- todo: add versus modes for technomotion techno = "Single4,Single5,Single8,Double4,Double5,Double8", lights = "Single", -- lights shouldn't be playable } return Modes[CurGameName()] end local function upper_first_letter(s) local first_letter= s:match("([a-zA-Z])") return s:gsub(first_letter, first_letter:upper(), 1) end -- No more having a metric for every style for every game mode. -Kyz function ScreenSelectStyleChoices() local styles= GAMEMAN:GetStylesForGame(GAMESTATE:GetCurrentGame():GetName()) local choices= {} for i, style in ipairs(styles) do local name= style:GetName() local cap_name= upper_first_letter(name) -- couple-edit and threepanel don't seem like they should actually be -- selectable. -Kyz if name ~= "couple-edit" and name ~= "threepanel" then choices[#choices+1]= "name," .. cap_name .. ";style," .. name .. ";text," .. cap_name .. ";screen," .. Branch.AfterSelectStyle() end end return choices end -- No more having an xy for every style for every game mode. -Kyz function ScreenSelectStylePositions(count) local poses= {} local columns= 1 local choice_height= 96 local column_x= {_screen.cx, _screen.cx + 160} if count > 4 then column_x[1]= _screen.cx - 160 columns= 2 end if count > 8 then column_x[1]= _screen.cx - 240 column_x[2]= _screen.cx column_x[3]= _screen.cx + 240 columns= 3 end local num_per_column= {math.ceil(count/columns), math.floor(count/columns)} if count > 8 then if count % 3 == 0 then num_per_column[3]= count/columns elseif count % 3 == 1 then num_per_column[3]= num_per_column[2] else num_per_column[3]= num_per_column[1] end end for c= 1, columns do local start_y= _screen.cy - (choice_height * ((num_per_column[c] / 2)+.5)) for i= 1, num_per_column[c] do poses[#poses+1]= {column_x[c], start_y + (choice_height * i)} end end return poses end function SelectProfileKeys() local sGame = CurGameName() if sGame == "pump" then return "Up,Down,Start,Back,Center,DownLeft,DownRight" elseif sGame == "dance" then return "Up,Down,Start,Back,Up2,Down2" else return "Up,Down,Start,Back" end end -- ScoreKeeperClass: -- [en] Determines the correct ScoreKeeper class to use. function ScoreKeeperClass() -- rave scorekeeper if GAMESTATE:GetPlayMode() == 'PlayMode_Rave' then return "ScoreKeeperRave" end if GAMESTATE:GetCurrentStyle() then local styleType = GAMESTATE:GetCurrentStyle():GetStyleType() if styleType == 'StyleType_TwoPlayersSharedSides' then return "ScoreKeeperShared" end end return "ScoreKeeperNormal" end -- ComboContinue: -- [en] function ComboContinue() local Continue = { dance = GAMESTATE:GetPlayMode() == "PlayMode_Oni" and "TapNoteScore_W2" or "TapNoteScore_W3", pump = "TapNoteScore_W3", beat = "TapNoteScore_W3", kb7 = "TapNoteScore_W3", para = "TapNoteScore_W4" } return Continue[CurGameName()] or "TapNoteScore_W3" end function ComboMaintain() local Maintain = { dance = "TapNoteScore_W3", pump = "TapNoteScore_W4", beat = "TapNoteScore_W3", kb7 = "TapNoteScore_W3", para = "TapNoteScore_W4" } return Maintain[CurGameName()] or "TapNoteScore_W3" end function ComboPerRow() sGame = CurGameName() if sGame == "pump" then return true elseif GAMESTATE:GetPlayMode() == "PlayMode_Oni" then return true else return false end end function EvalUsesCheckpointsWithJudgments() return (CurGameName() == "pump") and true or false end local ComboThresholds = { dance = { Hit = 2, Miss = 2, Fail = -1 }, pump = { Hit = 4, Miss = 4, Fail = 51 }, beat = { Hit = 1, Miss = 0, Fail = -1 }, kb7 = { Hit = 1, Miss = 0, Fail = -1 }, para = { Hit = 2, Miss = 0, Fail = -1 }, maniax = { Hit = 5, Miss = 0, Fail = -1 }, ------------------------------------------- default = { Hit = 2, Miss = 2, Fail = -1 } } function HitCombo() if ComboThresholds[CurGameName()] then return ComboThresholds[CurGameName()].Hit end return ComboThresholds["default"].Hit end function MissCombo() if ComboThresholds[CurGameName()] then return ComboThresholds[CurGameName()].Miss end return ComboThresholds["default"].Miss end -- FailCombo: -- [en] The combo that causes game failure. function FailCombo() -- ITG (dance) uses 30. Pump Pro uses 30, real Pump uses 51 if ComboThresholds[CurGameName()] then return ComboThresholds[CurGameName()].Fail end return ComboThresholds["default"].Fail end function TwoPartSelection() return GAMESTATE:GetCurrentGame():GetName() == "pump" and true or false end local RoutineSkins = { dance = { P1 = "midi-routine-p1", P2 = "midi-routine-p2" }, pump = { P1 = "cmd-routine-p1", P2 = "cmd-routine-p2" }, kb7 = { P1 = "default", P2 = "retrobar" }, ------------------------------------------------------------- default = { P1 = "default", P2 = "default" } } function RoutineSkinP1() if RoutineSkins[CurGameName()] then return RoutineSkins[CurGameName()].P1 end return RoutineSkins["default"].P1 end function RoutineSkinP2() if RoutineSkins[CurGameName()] then return RoutineSkins[CurGameName()].P2 end return RoutineSkins["default"].P2 end -- todo: use tables for some of these -aj function HoldTiming() return IsGame("pump") and 0 or PREFSMAN:GetPreference("TimingWindowSecondsHold") end function ShowHoldJudgments() return not IsGame("pump") end local CodeDetectorCodes = { -- steps PrevSteps1 = { default = "", dance = "Up,Up", pump = "+UpLeft", }, PrevSteps2 = { default = "MenuUp,MenuUp", dance = "MenuUp,MenuUp", pump = "", }, NextSteps1 = { default = "", dance = "Down,Down", pump = "+UpRight", }, NextSteps2 = { default = "MenuDown,MenuDown", dance = "MenuDown,MenuDown", pump = "", }, -- group NextGroup = { default = "", dance = "MenuUp,MenuRight,MenuRight", pump = "", }, PrevGroup = { default = "", dance = "MenuUp,MenuDown,MenuUp,MenuDown", pump = "", }, CloseCurrentFolder = { default = "MenuUp-MenuDown", dance = "Up-Down", pump = "@UpLeft-@UpRight-Center", }, -- OptionsList PrevOptionsList = { default = "@MenuUp-MenuDown", }, NextOptionsList = { default = "@MenuDown-MenuUp", }, -- sorts NextSort1 = { default = "@MenuLeft-@MenuRight-Start", dance = "@MenuLeft-@MenuRight-Start", pump = "@MenuLeft-@MenuRight-Start", }, NextSort2 = { default = "MenuLeft-MenuRight", dance = "MenuLeft-MenuRight", pump = "MenuLeft-MenuRight", }, NextSort3 = { default = "", dance = "@Left-@Right-Start", pump = "@DownLeft-@DownRight-Start", }, NextSort4 = { default = "", dance = "Left-Right", pump = "DownLeft-DownRight", }, -- modemenu ModeMenu1 = { default = "", dance = "Up,Down,Up,Down", }, ModeMenu2 = { default = "MenuUp,MenuDown,MenuUp,MenuDown", }, -- Evaluation: SaveScreenshot1 = { default = "MenuLeft-MenuRight", }, SaveScreenshot2 = { default = "Select", }, -- modifiers section CancelAll = { default = "", dance = "Left,Right,Left,Right,Left,Right,Left,Right", }, --- specific modifiers Mirror = { default = "", dance = "", pump = "DownRight,DownLeft,UpRight,UpLeft,DownRight,DownLeft,UpRight,UpLeft,Center", }, Left = { default = "", dance = "", }, Right = { default = "", dance = "", }, Shuffle = { default = "", dance = "", pump = "UpLeft,UpRight,UpLeft,UpRight,DownLeft,DownRight,DownLeft,DownRight,Center", -- random }, SuperShuffle = { default = "", dance = "", pump = "UpLeft,UpRight,DownLeft,DownRight,UpLeft,UpRight,DownLeft,DownRight,Center" }, Reverse = { default = "", dance = "", pump = "UpLeft,DownLeft,UpRight,DownRight,UpLeft,DownLeft,UpRight,DownRight,DownRight", -- drop }, HoldNotes = { default = "", dance = "", }, Mines = { default = "", }, Dark = { default = "", }, Hidden = { default = "", pump = "UpLeft,UpRight,DownLeft,DownRight,Center", -- vanish }, RandomVanish = { default = "", }, -- boost (accel), brake (decel), stealth (nonstep) --- next/prev modifiers NextTransform = { default = "", }, NextScrollSpeed = { default = "", dance = "", pump = "UpLeft,UpRight,UpLeft,UpRight,Center", }, PreviousScrollSpeed = { default = "", dance = "", pump = "UpRight,UpLeft,UpRight,UpLeft,Center", }, NextAccel = { default = "", dance = "", }, NextEffect = { default = "", dance = "", }, NextAppearance = { default = "", dance = "", }, NextTurn = { default = "", }, -- cancel all in player options CancelAllPlayerOptions = { default = "", dance = "Left,Right,Left,Right,Left,Right", }, }; function GetCodeForGame(codeName) local gameName = string.lower(CurGameName()) local inputCode = CodeDetectorCodes[codeName] return inputCode[gameName] or inputCode["default"] end local OptionsListKeys = { PrevItem = { pump="MenuLeft", default="MenuUp" }, NextItem = { pump="MenuRight", default="MenuDown" } }; function GetOptionsListMapping(name) local sGame = string.lower(GAMESTATE:GetCurrentGame():GetName()) local map = OptionsListKeys[name] return map[sGame] or map["default"] end function oitg_zoom_mode_actor() return Def.Actor{ OnCommand= function(self) local screen= SCREENMAN:GetTopScreen() for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do local player= screen:GetChild("Player"..ToEnumShortString(pn)) if player and player.set_oitg_zoom_mode then player:set_oitg_zoom_mode(true) end end end, } end