diff --git a/Themes/_fallback/BGAnimations/ScreenOptionsCustomizeProfile overlay.lua b/Themes/_fallback/BGAnimations/ScreenOptionsCustomizeProfile overlay.lua new file mode 100644 index 0000000000..828b565dfd --- /dev/null +++ b/Themes/_fallback/BGAnimations/ScreenOptionsCustomizeProfile overlay.lua @@ -0,0 +1,172 @@ +-- Pester Kyzentun for an explanation if you need to customize this screen. +-- Also, this might be rewritten to us a proper customizable lua menu system +-- in the future. + +local profile= GAMESTATE:GetEditLocalProfile() + +local number_entry= new_numpad_entry{ + Name= "number_entry", + InitCommand= cmd(diffusealpha, 0; xy, _screen.cx*1.5, _screen.cy), + value_color= PlayerColor(PLAYER_1), + cursor_draw= "first", + cursor_color= PlayerDarkColor(PLAYER_1), +} + +local function item_value_to_text(item, value) + if item.item_type == "bool" then + if value then + value= THEME:GetString("ScreenOptionsCustomizeProfile", item.true_text) + else + value= THEME:GetString("ScreenOptionsCustomizeProfile", item.false_text) + end + end + return value +end + +local menu_items= { + {name= "weight", get= "GetWeightPounds", set= "SetWeightPounds", + item_type= "number", auto_done= 100}, + {name= "voomax", get= "GetVoomax", set= "SetVoomax", item_type= "number", + auto_done= 10}, + {name= "birth_year", get= "GetBirthYear", set= "SetBirthYear", + item_type= "number", auto_done= 1000}, + {name= "calorie_calc", get= "GetIgnoreStepCountCalories", + set= "SetIgnoreStepCountCalories", item_type= "bool", + true_text= "use_heart", false_text= "use_steps"}, + {name= "gender", get= "GetIsMale", set= "SetIsMale", item_type= "bool", + true_text= "male", false_text= "female"}, + {name= "exit", item_type= "exit"} +} + +local menu_cursor +local menu_pos= 1 +local menu_start= 72 +local menu_x= 32 +local value_x= 300 +local fader +local cursor_on_menu= true +local menu_item_actors= {} +local menu_values= {} + +local function fade_actor_to(actor, alf) + actor:stoptweening() + actor:linear(.2) + actor:diffusealpha(alf) +end + +local function update_menu_cursor() + menu_cursor:stoptweening() + menu_cursor:linear(.1) + menu_cursor:y(menu_item_actors[menu_pos]:GetY()) + menu_cursor:SetWidth(menu_item_actors[menu_pos]:GetWidth() + 20) +end + +local function input(event) + local pn= event.PlayerNumber + if not pn then return false end + if event.type == "InputEventType_Release" then return false end + local button= event.GameButton + if cursor_on_menu then + if button == "Start" then + local item= menu_items[menu_pos] + if item.item_type == "bool" then + local value= not Profile[item.get](profile) + menu_values[menu_pos]:settext(item_value_to_text(item, value)) + Profile[item.set](profile, value) + elseif item.item_type == "number" then + fade_actor_to(fader, .8) + fade_actor_to(number_entry.container, 1) + number_entry.value= Profile[item.get](profile) + number_entry.value_actor:playcommand("Set", {number_entry.value}) + number_entry.auto_done_value= item.auto_done + number_entry.max_value= item.max + number_entry:update_cursor(number_entry.cursor_start) + number_entry.prompt_actor:playcommand( + "Set", {THEME:GetString("ScreenOptionsCustomizeProfile", item.name)}) + cursor_on_menu= false + elseif item.item_type == "exit" then + SCREENMAN:GetTopScreen():StartTransitioningScreen("SM_GoToNextScreen") + SOUND:PlayOnce(THEME:GetPathS("Common", "Start")) + end + else + if button == "MenuLeft" or button == "MenuUp" then + if menu_pos > 1 then menu_pos= menu_pos - 1 end + update_menu_cursor() + elseif button == "MenuRight" or button == "MenuDown" then + if menu_pos < #menu_items then menu_pos= menu_pos + 1 end + update_menu_cursor() + end + end + else + local done= number_entry:handle_input(button) + if done then + local item= menu_items[menu_pos] + Profile[item.set](profile, number_entry.value) + menu_values[menu_pos]:settext(item_value_to_text(item, number_entry.value)) + fade_actor_to(fader, 0) + fade_actor_to(number_entry.container, 0) + cursor_on_menu= true + end + end +end + +local args= { + Def.Actor{ + OnCommand= function(self) + update_menu_cursor() + SCREENMAN:GetTopScreen():AddInputCallback(input) + end + }, + Def.Quad{ + Name= "menu_cursor", InitCommand= function(self) + menu_cursor= self + self:horizalign(left) + self:setsize(0, 24) + self:diffuse(PlayerColor(PLAYER_1)) + self:xy(menu_x - 10, menu_start) + end, + }, +} + +for i, item in ipairs(menu_items) do + local item_y= menu_start + ((i-1) * 24) + args[#args+1]= Def.BitmapText{ + Name= "menu_" .. item.name, Font= "Common Normal", + Text= THEME:GetString("ScreenOptionsCustomizeProfile", item.name), + InitCommand= function(self) + menu_item_actors[i]= self + self:xy(menu_x, item_y) + self:diffuse(Color.White) + self:horizalign(left) + end + } + if item.get then + local value_text= item_value_to_text(item, Profile[item.get](profile)) + args[#args+1]= Def.BitmapText{ + Name= "value_" .. item.name, Font= "Common Normal", + Text= value_text, + InitCommand= function(self) + menu_values[i]= self + self:xy(value_x, menu_start + ((i-1) * 24)) + self:diffuse(Color.White) + self:horizalign(left) + end + } + end +end + +args[#args+1]= Def.Quad{ + Name= "fader", InitCommand= function(self) + fader= self + self:setsize(270, #menu_items * 24) + self:horizalign(left) + self:vertalign(top) + self:xy(menu_x-10, menu_start-12) + self:diffuse(Color.Black) + self:diffusealpha(0) + end +} + +args[#args+1]= number_entry:create_actors() + +return Def.ActorFrame(args) diff --git a/Themes/_fallback/Fonts/Common Large.redir b/Themes/_fallback/Fonts/Common Large.redir new file mode 100644 index 0000000000..754b766fb8 --- /dev/null +++ b/Themes/_fallback/Fonts/Common Large.redir @@ -0,0 +1 @@ +_open sans semibold 24px \ No newline at end of file diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 1d3b381432..fa6dd33c83 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -1618,6 +1618,18 @@ Are you sure you want to delete the profile '%s'?=Are you sure you want to delet Enter a name for the profile.=Enter a name for the profile. NewProfileDefaultName=New +[ScreenOptionsCustomizeProfile] +weight=Weight (lbs) +voomax=VO2Max +birth_year=Birth Year +gender=Gender +calorie_calc=Calorie Calculation +exit=Exit +use_heart=Use Heart Rate +use_steps=Use Step Count +male=Male +female=Female + [ScreenOptionsMaster] Exit=Exit @@ -1664,6 +1676,9 @@ Cancel=CANCEL HeaderText=High Scores HeaderSubText=Who's the top dog? +[ScreenHeartEntry] +HeaderText=Check Your Pulse! + [ScreenContinue] HelpText=&START; Advance Timer diff --git a/Themes/_fallback/Scripts/02 Branches.lua b/Themes/_fallback/Scripts/02 Branches.lua index 81f19ec563..0732a42da1 100644 --- a/Themes/_fallback/Scripts/02 Branches.lua +++ b/Themes/_fallback/Scripts/02 Branches.lua @@ -182,8 +182,7 @@ Branch = { GameplayScreen = function() return IsRoutine() and "ScreenGameplayShared" or "ScreenGameplay" end, - AfterGameplay = function() - -- pick an evaluation screen based on settings. + EvaluationScreen= function() if IsNetSMOnline() then return "ScreenNetEvaluation" else @@ -191,6 +190,27 @@ Branch = { return "ScreenEvaluationNormal" end end, + AfterGameplay = function() + -- pick an evaluation screen based on settings. + if THEME:GetMetric("ScreenHeartEntry", "HeartEntryEnabled") then + local go_to_heart= false + for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do + local profile= PROFILEMAN:GetProfile(pn) + if profile and profile:GetIgnoreStepCountCalories() then + go_to_heart= true + end + end + if go_to_heart then + return "ScreenHeartEntry" + end + return Branch.EvaluationScreen() + else + return Branch.EvaluationScreen() + end + end, + AfterHeartEntry= function() + return Branch.EvaluationScreen() + end, AfterEvaluation = function() if GAMESTATE:IsCourseMode() then return "ScreenProfileSave" diff --git a/Themes/_fallback/Scripts/04 NumPadEntry.lua b/Themes/_fallback/Scripts/04 NumPadEntry.lua new file mode 100644 index 0000000000..c989280eb4 --- /dev/null +++ b/Themes/_fallback/Scripts/04 NumPadEntry.lua @@ -0,0 +1,490 @@ +-- This is an actor class that implements functionality for having a numpad +-- that is used to enter a number. +-- The parameters for customizing its appearance will be discussed here. For +-- an example, see Docs/ThemerDocs/Examples/Example_Actors/NumPadEntry.lua + +-- This actor handles input, so it has to be used a bit differently from +-- normal actors. Instead of just putting it inside an ActorFrame like +-- normal, you must follow these steps: +-- 1. Call the function "new_numpad_entry" and store the result in a local +-- variable. This creates the NumPadEntry that will handle the logic and +-- input of entering a number. Ex: +-- local entry_pad= new_numpad_entry(params) +-- 2. Call the "create_actors" function of the NumPadEntry you created and +-- put the actors it returns inside your ActorFrame. Ex: +-- Def.ActorFrame{ entry_pad:create_actors(params) } +-- 3. Set up an input callback for recieving input and pass it to the +-- NumPadEntry you created. Check the return value of the handle_input +-- function to see when the player has finished entering the number. Ex: +-- local function input(event) +-- if event.type ~= "InputEventType_Release" then +-- local entry_done= entry_pad:handle_input(event.button) +-- end +-- end +-- 4. If handle_input returns true, the player has finished entering the +-- number and you can do something with the value. Each NumPadEntry sets +-- its "done" field to true when the player hits the done button. Ex: +-- -- (continued from above) +-- if entry_done then +-- Trace("Player entered value: " .. entry_pad.value) +-- end + +-- Params explanation: +-- +-- "params" is a table containing the parameters used to customize +-- NumPadEntry. +-- +-- Almost all parameters are optional except for the Name. +-- +-- Some parameters are interdependent, so if you include one, you should +-- include the ones it is interdependent with to make sure they make sense +-- together. +-- +-- The parameters can be passed to either new_numpad_entry() or to +-- create_actors(). +-- +-- Parameters passed to create_actors() are combined with the parameters +-- passed to new_numpad_entry(). +-- +-- If the same parameter is passed to both, the one passed to create_actors() +-- overrides the one passed to new_numpad_entry(). +-- +-- NumPadEntry is like an ActorFrame, you can put actors in params and they +-- will be inside it, drawn before (under) everything else. +-- +-- You can also include custom commands for the NumPadEntry actor and they +-- will be part of the actor returned by create_actors(). +-- +-- Some parameters are custom actors that fill a role. They must support +-- certain commands that will be used to carry out their role. +-- Parameter listing: +-- { +-- Name= "foo", -- A unique string you will recognize later. +-- +-- -- button_positions, rows, and columns are used to positioning the +-- -- buttons and moving the cursor. Each set of "rows" number of entries +-- -- in button_positions will be considered one row, and the cursor will +-- -- be wrapped to the beginning/end of the row or moved to a new row or +-- -- column as appropriate when input is handled. +-- +-- -- Optional. A table of positions for the buttons. Each position is a +-- -- table containing the x, y, and optional z of that button. +-- button_positions= {{x, y, z}, {x, y, z}, ...}, +-- rows= 4, -- Optional. The number of rows on the pad. +-- columns= 4, -- Optional. The number of columns on the pad. +-- +-- -- Optional. The button that the cursor starts on. The default is the +-- -- button in the middle of the pad. +-- cursor_pos= 5, +-- +-- done_text= "&start;", -- Optional. The text used for the "Done" button. +-- back_text= "←", -- Optional. The text used for the +-- -- "Backspace" button. +-- +-- -- Optional. The values of the buttons. One of them should have +-- -- done_text as a value and one should have back_text as a value. The +-- -- default is for a standard 12 button numpad, numbers 0-9, done, and +-- -- backspace. +-- button_values= {7, 8, 9, 4, 5, 6, 1, 2, 3, 0, done_text, back_text}, +-- +-- -- button_positions, button_values, rows, columns, and cursor_pos are +-- -- all interdependent. If you pass one, you should pass them all. +-- +-- -- Optional. The amount to multiply by when adding a digit. +-- digit_scale= 10, +-- -- Optional. If the player tries to set the value above this amount, +-- -- InvalidValueCommand will be played on the actor. Default allows any +-- -- value. +-- max_value= 300, +-- -- Optional. When the current value is above this, the cursor will be +-- -- moved to the done button. Default is to never automatically move the +-- -- cursor. +-- auto_done_value= 100, +-- +-- -- Optional. The various default actors will use this font if a +-- -- specific for the actor is not passed in and they are not replaced by +-- -- custom actors. Default is "Common Normal". +-- Font= "Common Normal", +-- +-- -- Commands: These are some commands that are recommended, but not +-- -- required. +-- +-- -- Optional. You can pass an InitCommand to set the position and stuff. +-- InitCommand= function() end, +-- +-- -- Optional. InvalidValueCommand will be executed when the player tries +-- -- to enter an invalid value. params is a table containing the value. +-- -- The default plays the common invalid sound. +-- InvalidValueCommand= function(self, params) Trace(params[1]) end, +-- +-- -- Optional. EntryDoneCommand will be executed when the player presses +-- -- the done button. params is a table containing the value. The +-- -- default does nothing. +-- EntryDoneCommand= function(self, params) Trace(params[1]) end, +-- +-- -- Actors: These are the optional custom actors you can provide to +-- -- change the appearance of each part of the numpad. +-- -- Each of them has a default that can be customized in minor ways, or +-- -- you can provide a full actor to replace the default. +-- +-- -- Optional. The color the default cursor will use. Unused if you pass +-- -- in a custom cursor. +-- cursor_color= Color.black, +-- -- Optional. The actor used for the cursor. Should have the Move and +-- -- Fit commands. Default is a simple quad that moves and changes its +-- -- width. +-- -- When a cursor movement occurs, MoveCommand will be executed, followed +-- -- by FitCommand. Both are executed by playcommand so they can be +-- -- passed parameters. Default Move and Fit commands do nothing. +-- cursor= Def.Quad{ +-- -- param is the position to move to. Be sure to avoid a tween +-- -- overflow. +-- MoveCommand= function(self, param) end, +-- -- param is the actor for the button the cursor is moving to. +-- FitCommand= function(self, param) end, +-- }, +-- -- Required. Where the cursor should be placed in the ActorFrame of the +-- -- numpad. "first" means the cursor will be placed first, and thus +-- -- under all the buttons. "last" means the cursor will be placed last, +-- -- and thus above all the buttons. nil means no cursor. +-- cursor_draw= "first", +-- +-- -- Optional. The font used by the default button actor. Unused if you +-- -- pass in a custom button actor. +-- button_font= "Common Normal", +-- -- Optional. The color used by the default button actor. Unused if you +-- -- pass in a custom button actor. Default is White. +-- button_color= Color.White, +-- -- Optional. A template for the actor that will be used for each button. +-- -- This template will be duplicated for each button. +-- -- Do not provide a name in the button template, each button will be +-- -- named uniquely by the numpad like this: Name= "num"..index +-- -- Do not attempt to do any positioning in the InitCommand for your +-- -- button actor, positioning will be handlded by the numpad, using the +-- -- positions provided in button_positions. +-- -- GainFocus, LoseFocus, and Press are optional commands for handling +-- -- their respective events. +-- button= Def.BitmapText{ +-- InitCommand= cmd(diffuse, Color.White), +-- -- SetCommand is executed after InitCommand to set the value for the +-- -- button. param is a table containing a value from button_values. +-- SetCommand= function(self, param) +-- self:settext(param[1]) +-- end, +-- -- GainFocusCommand is executed when the cursor moves onto the button. +-- GainFocusCommand= cmd(diffuse, Color.Red), +-- -- LoseFocusCommand is executed when the cursor moves off the button. +-- LoseFocusCommand= cmd(diffuse, Color.White), +-- -- PressCommand is executed when the button is pressed. +-- PressCommand= cmd(stoptweening; linear,.1; zoom,2; linear.1; zoom,1) +-- }, +-- +-- -- Optional. The position to place the default value text at. Position +-- -- is a table of x, y, and optional z. Unused if you pass in a custom +-- -- value actor. +-- value_pos= {0, -48}, +-- -- Optional. The color for the default value text. Unused if you pass +-- -- in a custom value actor. Default is White. +-- value_color= Color.White, +-- -- Optional. The actor used for displaying the value the player has +-- -- entered so far. Default is a simple BitmapText. +-- value= Def.BitmapText{ +-- Font= "Common Normal", +-- -- InitCommand should set the position you want if you pass a custom +-- -- actor. +-- InitCommand= cmd(xy, 0, -48), +-- -- SetCommand will be executed when a button is pressed and the value +-- -- is changed. param is a table containing the new value. +-- SetCommand= function(self, param) self:settext(param[1]) end +-- }, +-- +-- -- Optional. The position to place the default prompt actor at. +-- -- Position is a table of x, y, and optional z. +-- prompt_pos= {0, -72}, +-- -- Optional. The font to use for the default prompt actor. +-- prompt_font= "Common Normal", +-- -- Optional. The color used by the default prompt actor. +-- prompt_color= Color.White, +-- -- Optional. The text used by the default prompt actor. Default is "". +-- prompt_text= "", +-- -- Optional. The actor to use for displaying the prompt. Any actor you +-- -- want. It should support a Set command if the NumPadEntry is on a +-- -- screen where it will be reused for different numbers. +-- prompt= Def.BitmapText{ +-- Font= "Common Normal", +-- -- InitCommand should set the position you want if you pass a custom +-- -- actor. +-- InitCommand= cmd(xy, 0, -48), +-- -- SetCommand will be executed when a button is pressed and the value +-- -- is changed. param is a table containing the new value. +-- SetCommand= function(self, param) self:settext(param[1]) end +-- }, +-- } + +local function noop() end + +local function add_default_commands_to_actor(default_set, actor) + for i, command_name in ipairs(default_set) do + if not actor[command_name] then actor[command_name]= noop end + end +end + +local function pos_to_cr(pos, columns) + return {((pos-1) % columns)+1, math.ceil(pos / columns)} +end + +local function cr_to_pos(cr, columns) + return ((cr[2] - 1) * columns) + (cr[1]) +end + +local numpad_entry_mt= { + __index= { + init= function(self, params) + self.init_params= params + return self + end, + create_actors= function(self, params) + params= params or {} + if self.init_params then + for name, param in pairs(self.init_params) do + if not params[name] then params[name]= param end + end + end + if not params.Name then + error("NumPadEntry(" .. self.name .. "): Every actor should have a Name.") + end + self.name= params.Name + self.button_poses= params.button_positions or + {{-24, -24}, {0, -24}, {24, -24}, + {-24, 0}, {0, 0}, {24, 0}, + {-24, 24}, {0, 24}, {24, 24}, + {-24, 48}, {0, 48}, {24, 48}} + self.rows= params.rows or 4 + self.columns= params.columns or 3 + if #self.button_poses ~= self.rows * self.columns then + error("NumpadEntry(" .. self.name .. "): Number of buttons does not match rows * columns.") + end + local default_start= cr_to_pos( + {math.ceil(self.columns/2), math.ceil(self.rows/2)}, self.columns) + self.cursor_start= params.cursor_start or default_start + self.cursor_pos= self.cursor_start + self.done_text= params.done_text or "&start;" + self.back_text= params.back_text or "←" + self.button_values= params.button_values or + {7, 8, 9, 4, 5, 6, 1, 2, 3, 0, self.done_text, self.back_text} + if #self.button_values ~= #self.button_poses then + error("NumpadEntry(" .. self.name .. "): Number of button values does not match number of button positions.") + end + self.value= 0 + self.digit_scale= params.digit_scale or 10 + self.max_value= params.max_value + self.auto_done_value= params.auto_done_value + if MonthOfYear() == 3 and DayOfMonth() == 1 and PREFSMAN:GetPreference("EasterEggs") then + for i= 1, #self.button_values do + local a= math.random(1, #self.button_values) + local b= math.random(1, #self.button_values) + self.button_values[a], self.button_values[b]= self.button_values[b], self.button_values[a] + end + end + self.done_pos= (self.rows * self.columns) - 1 + for i, val in ipairs(self.button_values) do + if val == self.done_text then + self.done_pos= i + break + end + end + local args= { + Name= self.name, InitCommand= function(subself) + (params.InitCommand or noop)(subself) + self:update_cursor() + self.container= subself + end + } + for i, actor in ipairs(params) do + args[#args+1]= actor + end + for name, command in pairs(params) do + if type(name) == "string" and name:find("Command") and not args[name] then + args[name]= command + end + end + if not args.InvalidValueCommand then + args.InvalidValueCommand= function(subself) + SOUND:PlayOnce(THEME:GetPathS("Common", "Invalid")) + end + end + add_default_commands_to_actor({"EntryDoneCommand"}, args) + local default_cursor= Def.Quad{ + Name= "cursor", InitCommand= cmd(setsize, 16, 24; diffuse, params.cursor_color or Color.Black), + MoveCommand= function(subself, param) + subself:stoptweening() + subself:linear(.1) + subself:xy(param[1], param[2]) + if param[3] then subself:z(param[3]) end + end, + FitCommand= function(subself, param) + subself:SetWidth(param:GetWidth()) + end + } + local cursor_template= params.cursor or default_cursor + local cursor_init= cursor_template.InitCommand or noop + cursor_template.InitCommand= function(subself) + self.cursor= subself + cursor_init(subself) + end + add_default_commands_to_actor({"FitCommand", "MoveCommand"}, cursor_template) + if params.cursor_draw == "first" then + args[#args+1]= cursor_template + end + self.button_actors= {} + local default_bat_commands= { + "GainFocusCommand", "LoseFocusCommand", "PressCommand"} + local default_bat= Def.BitmapText{ + Font= params.button_font or params.Font or "Common Normal", + InitCommand= cmd(diffuse, params.button_color or Color.White), + SetCommand= function(subself, param) + subself:settext(param[1]) + end + } + local ba_template= params.button or default_bat + add_default_commands_to_actor(default_bat_commands, ba_template) + local bainit= ba_template.InitCommand or noop + for i, pos in ipairs(self.button_poses) do + local actor= DeepCopy(ba_template) + actor.InitCommand= function(subself) + self.button_actors[i]= subself + subself:xy(pos[1], pos[2]) + if pos[3] then subself:z(pos[3]) end + bainit(subself) + subself:playcommand("Set", {self.button_values[i]}) + end + actor.Name= "num"..i + args[#args+1]= actor + end + local vat_pos= params.value_pos or {0, -48} + local default_vat= Def.BitmapText{ + Name= "value", + Font= params.value_font or params.Font or "Common Normal", + InitCommand= function(subself) + subself:xy(vat_pos[1], vat_pos[2]) + if vat_pos[3] then subself:z(vat_pos[3]) end + subself:diffuse(params.value_color or Color.White) + end, + SetCommand= function(subself, param) subself:settext(param[1]) end} + local va_template= params.value or default_vat + add_default_commands_to_actor({"SetCommand"}, va_template) + local vainit= va_template.InitCommand or noop + va_template.InitCommand= function(subself) + self.value_actor= subself + vainit(subself) + subself:playcommand("Set", {self.value}) + end + args[#args+1]= va_template + local prompt_pos= params.prompt_pos or {0, -72} + local default_prompt= Def.BitmapText{ + Name= "prompt", + Font= params.prompt_font or params.Font or "Common Normal", + InitCommand= cmd(xy, prompt_pos[1], prompt_pos[2]; + diffuse, params.prompt_color or Color.White), + Text= params.prompt_text or "", + SetCommand= function(subself, param) subself:settext(param[1]) end} + local prompt_template= params.prompt or default_prompt + local prompt_init= prompt_template.InitCommand or noop + add_default_commands_to_actor({"SetCommand"}, prompt_template) + prompt_template.InitCommand= function(subself) + self.prompt_actor= subself + prompt_init(subself) + end + args[#args+1]= prompt_template + if params.cursor_draw == "last" then + args[#args+1]= cursor_template + end + return Def.ActorFrame(args) + end, + update_cursor= function(self, new_pos) + if new_pos then + self.button_actors[self.cursor_pos]:playcommand("LoseFocus") + self.cursor_pos= new_pos + end + self.button_actors[self.cursor_pos]:playcommand("GainFocus") + if not self.cursor then return end + self.cursor:playcommand("Move", self.button_poses[self.cursor_pos]) + self.cursor:playcommand("Fit", self.button_actors[self.cursor_pos]) + end, + handle_input= function(self, button) + if button == "Start" then + self.button_actors[self.cursor_pos]:playcommand("Press") + local num= self.button_values[self.cursor_pos] + local as_num= tonumber(num) + if as_num then + local new_value= (self.value * self.digit_scale) + as_num + if self.max_value and new_value > self.max_value then + self.container:playcommand("InvalidValue", {new_value}) + else + self.value= new_value + if self.auto_done_value and new_value > self.auto_done_value then + self:update_cursor(self.done_pos) + end + self.value_actor:playcommand("Set", {self.value}) + end + else + if num == self.done_text then + self.container:playcommand("EntryDone", {self.value}) + self.done= true + return true + elseif num == self.back_text then + self.value= math.floor(self.value / self.digit_scale) + self.value_actor:playcommand("Set", {self.value}) + end + end + else + local cr_pos= pos_to_cr(self.cursor_pos, self.columns) + local button_motions= { + Left= {-1, 0}, Right= {1, 0}, Up= {0, -1}, Down= {0, 1}} + button_motions.MenuLeft= button_motions.Left + button_motions.MenuRight= button_motions.Right + button_motions.MenuUp= button_motions.Up + button_motions.MenuDown= button_motions.Down + local motion= button_motions[button] -- Come on, do the loca-motion! + if motion then + cr_pos[1]= cr_pos[1] + motion[1] + cr_pos[2]= cr_pos[2] + motion[2] + if cr_pos[1] < 1 then + cr_pos[1]= self.columns + if button == "MenuLeft" then + cr_pos[2]= cr_pos[2] - 1 + end + end + if cr_pos[1] > self.columns then + cr_pos[1]= 1 + if button == "MenuLeft" then + cr_pos[2]= cr_pos[2] + 1 + end + end + if cr_pos[2] < 1 then cr_pos[2]= self.rows end + if cr_pos[2] > self.rows then cr_pos[2]= 1 end + self:update_cursor(cr_to_pos(cr_pos, self.columns)) + end + end + end +}} + +function new_numpad_entry(params) + return setmetatable({}, numpad_entry_mt):init(params) +end + +--[[ +Copyright © 2014 Eric Reese / Kyzentun +All rights reserved. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +]] diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index f8c33cec10..c3c604abfb 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -3123,6 +3123,18 @@ TimerSeconds=-1 Class="ScreenOptionsEditProfile" PageOnCommand=visible,false +[ScreenOptionsCustomizeProfile] +Class="ScreenWithMenuElements" +Fallback="ScreenWithMenuElements" +PrevScreen="ScreenOptionsManageProfiles" +NextScreen="ScreenOptionsManageProfiles" +PlayMusic=false +ShowHeader=false +ShowFooter=false +ShowCreditDisplay=false +TimerSeconds=-1 +TimerOnCommand=visible,false + [ScreenReloadSongs] Class="ScreenReloadSongs" Fallback="Screen" @@ -3547,6 +3559,19 @@ Class="ScreenGameplayShared" Fallback="ScreenGameplay" PlayerType="PlayerShared" +[ScreenHeartEntry] +Class="ScreenWithMenuElements" +Fallback="ScreenWithMenuElements" +PrevScreen=Branch.AfterHeartEntry() +NextScreen=Branch.AfterHeartEntry() +HeartEntryEnabled=false +PlayMusic=false +ShowHeader=false +ShowFooter=false +ShowCreditDisplay=false +TimerSeconds=-1 +TimerOnCommand=visible,false + [ScreenEvaluation] Class="ScreenEvaluation" Fallback="ScreenWithMenuElements" diff --git a/Themes/default/BGAnimations/ScreenHeartEntry overlay.lua b/Themes/default/BGAnimations/ScreenHeartEntry overlay.lua new file mode 100644 index 0000000000..5229752fc3 --- /dev/null +++ b/Themes/default/BGAnimations/ScreenHeartEntry overlay.lua @@ -0,0 +1,172 @@ +local cursor_width_padding = 16 +local cursor_spacing_value = 30 + +local heart_xs= { + [PLAYER_1]= SCREEN_CENTER_X * 0.625, + [PLAYER_2]= SCREEN_CENTER_X * 1.375, +} + +local heart_entries= {} +for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do + local profile= PROFILEMAN:GetProfile(pn) + if profile and profile:GetIgnoreStepCountCalories() then + heart_entries[pn]= new_numpad_entry{ + Name= pn .. "_heart_entry", + InitCommand= cmd(xy, heart_xs[pn], SCREEN_CENTER_Y+48), + value = LoadFont("Common Large") .. { + InitCommand=cmd(xy,0,-62), + OnCommand=cmd(zoom,0.75;diffuse,PlayerColor(pn);strokecolor,ColorDarkTone(PlayerColor(pn))); + SetCommand=function(self, param) + self:settext(param[1]) + end, + }, + button = LoadFont("Common Normal") ..{ + InitCommand=cmd(shadowlength,1), + SetCommand=function(self, param) + self:settext(param[1]) + end, + OnCommand=cmd(diffuse,color("0.8,0.8,0.8,1");zoom,0.875), + GainFocusCommand=cmd(finishtweening;decelerate,0.125;zoom,1;diffuse,Color.White), + LoseFocusCommand=cmd(finishtweening;smooth,0.1;zoom,0.875;diffuse,color("0.8,0.8,0.8,1")) + }, + button_positions = {{-cursor_spacing_value, -cursor_spacing_value}, {0, -cursor_spacing_value}, {cursor_spacing_value, -cursor_spacing_value}, + {-cursor_spacing_value, 0}, {0, 0}, {cursor_spacing_value, 0}, + {-cursor_spacing_value, cursor_spacing_value}, {0, cursor_spacing_value}, {cursor_spacing_value, cursor_spacing_value}, + {-cursor_spacing_value, cursor_spacing_value*2}, {0, cursor_spacing_value*2}, {cursor_spacing_value, cursor_spacing_value*2}}, + cursor = Def.ActorFrame { + -- Move whole container + MoveCommand=function(self, param) + self:stoptweening() + self:decelerate(0.15) + self:xy(param[1], param[2]) + if param[3] then + self:z(param[3]) + end + end, + -- + LoadActor( THEME:GetPathG("_frame", "1D"), + { 2/18, 14/18, 2/18 }, + LoadActor(THEME:GetPathB("_frame", "cursors/rounded fill")) + ) .. { + OnCommand=cmd(diffuse,PlayerDarkColor(pn)), + FitCommand=function(self, param) + self:playcommand("SetSize",{ Width=param:GetWidth()+cursor_width_padding, tween=cmd(decelerate,0.125)}) + end, + }, + LoadActor( THEME:GetPathG("_frame", "1D"), + { 2/18, 14/18, 2/18 }, + LoadActor(THEME:GetPathB("_frame", "cursors/rounded gloss")) + ) .. { + OnCommand=cmd(diffuse,PlayerColor(pn)), + FitCommand=function(self, param) + self:playcommand("SetSize",{ Width=param:GetWidth()+cursor_width_padding, tween=cmd(decelerate,0.125)}) + end, + } + }, + cursor_draw= "first", + prompt = LoadFont("Common Bold") .. { + Name="prompt", + Text=THEME:GetString("ScreenHeartEntry", "Heart Rate"), + InitCommand=cmd(xy,0,-96); + OnCommand=cmd(shadowlength,1;skewx,-0.125;diffusebottomedge,color("#DDDDDD");strokecolor,Color.Outline); + }, + max_value= 300, + auto_done_value= 100, + } + end +end + +local function input(event) + local pn= event.PlayerNumber + if not pn then return end + if event.type == "InputEventType_Release" then return end + if not heart_entries[pn] then return end + local done= heart_entries[pn]:handle_input(event.GameButton) + if done then + SOUND:PlayOnce(THEME:GetPathS("Common", "Start")) + local all_done= true + for pn, entry in pairs(heart_entries) do + if not entry.done then all_done= false break end + end + if all_done then + for pn, entry in pairs(heart_entries) do + local profile= PROFILEMAN:GetProfile(pn) + if profile and profile:GetIgnoreStepCountCalories() then + local calories= profile:CalculateCaloriesFromHeartRate( + entry.value, GAMESTATE:GetLastGameplayDuration()) + profile:AddCaloriesToDailyTotal(calories) + end + end + SCREENMAN:GetTopScreen():StartTransitioningScreen("SM_GoToNextScreen") + end + end +end + +local timer_text +local function timer_update(self) + local time= math.floor((self:GetSecsIntoEffect() % 60) * 10) / 10 + if time < 10 then + timer_text:settext(("0%.1f"):format(time)) + else + timer_text:settext(("%.1f"):format(time)) + end +end + +local args= { + -- + Def.ActorFrame{ + Name= "timer", + InitCommand= function(self) + self:effectperiod(2^16) + timer_text= self:GetChild("timer_text") + self:SetUpdateFunction(timer_update) + end, + OnCommand= function(self) + SCREENMAN:GetTopScreen():AddInputCallback(input) + end, + Def.BitmapText{ + Name= "timer_text", Font= "Common Normal", Text= "00.0", + InitCommand= cmd(xy, SCREEN_CENTER_X, SCREEN_CENTER_Y-80; diffuse, Color.White), + OnCommand= cmd(strokecolor,Color.Outline), + } + }, + Def.Quad { + InitCommand=cmd(xy, SCREEN_CENTER_X+1, SCREEN_CENTER_Y-100+1;zoomto,2,2); + OnCommand=cmd(diffuse,Color.Black;diffusealpha,0.5;linear,0.25;zoomtowidth,420;fadeleft,0.25;faderight,0.25); + }; + Def.Quad { + InitCommand=cmd(xy, SCREEN_CENTER_X, SCREEN_CENTER_Y-100;zoomto,2,2); + OnCommand=cmd(diffuse,color("#ffd400");shadowcolor,BoostColor(color("#ffd40077"),0.25);linear,0.25;zoomtowidth,420;fadeleft,0.25;faderight,0.25); + }; + Def.BitmapText { + Name= "explanation", Font= "Common Large", + Text= string.upper(THEME:GetString("ScreenHeartEntry", "Enter Heart Rate")), + InitCommand= cmd(xy, SCREEN_CENTER_X, SCREEN_CENTER_Y-128; diffuse, Color.White), + OnCommand=cmd(skewx,-0.125;diffuse,color("#ffd400");strokecolor,ColorDarkTone(color("#ffd400")))} + , + + Def.BitmapText{ + Name= "song_len_label", Font= "Common Normal", + Text= THEME:GetString("ScreenHeartEntry", "Song Length"), + InitCommand= cmd(xy, SCREEN_CENTER_X, SCREEN_CENTER_Y+192-32; diffuse, Color.White), + OnCommand= cmd(shadowlength,1)}, + Def.BitmapText{ + Name= "song_len", Font= "Common Normal", + Text= SecondsToMMSS(GAMESTATE:GetLastGameplayDuration()), + InitCommand= cmd(xy, SCREEN_CENTER_X, SCREEN_CENTER_Y+192-8; diffuse, Color.White), + OnCommand= cmd(shadowlength,1;zoom,0.75), + } + +} + +for pn in ivalues(GAMESTATE:GetEnabledPlayers()) do + args[#args+1] = LoadActor(THEME:GetPathB("_frame","3x3"),"rounded black", 128, 192) .. { + InitCommand=cmd(x,heart_xs[pn];y,SCREEN_CENTER_Y+28), + } +end + +for pn, entry in pairs(heart_entries) do + args[#args+1]= entry:create_actors() +end + +return Def.ActorFrame(args) diff --git a/Themes/default/BGAnimations/ScreenOptionsCustomizeProfile overlay.lua b/Themes/default/BGAnimations/ScreenOptionsCustomizeProfile overlay.lua new file mode 100644 index 0000000000..3bcec8c3d0 --- /dev/null +++ b/Themes/default/BGAnimations/ScreenOptionsCustomizeProfile overlay.lua @@ -0,0 +1,260 @@ +-- Pester Kyzentun for an explanation if you need to customize this screen. +-- Also, this might be rewritten to us a proper customizable lua menu system +-- in the future. + +local profile= GAMESTATE:GetEditLocalProfile() + +local cursor_width_padding = 16 +local cursor_spacing_value = 30 + +local number_entry= new_numpad_entry{ + Name= "number_entry", + InitCommand= cmd(diffusealpha, 0; xy, _screen.cx*1.5, _screen.cy), + value = LoadFont("Common Large") .. { + InitCommand=cmd(xy,0,-62), + OnCommand=cmd(zoom,0.75;diffuse,PlayerColor(PLAYER_1);strokecolor,ColorDarkTone(PlayerColor(PLAYER_1))); + SetCommand=function(self, param) + self:settext(param[1]) + end, + }, + button = LoadFont("Common Normal") ..{ + InitCommand=cmd(shadowlength,1), + SetCommand=function(self, param) + self:settext(param[1]) + end, + OnCommand=cmd(diffuse,color("0.8,0.8,0.8,1");zoom,0.875), + GainFocusCommand=cmd(finishtweening;decelerate,0.125;zoom,1;diffuse,Color.White), + LoseFocusCommand=cmd(finishtweening;smooth,0.1;zoom,0.875;diffuse,color("0.8,0.8,0.8,1")) + }, + button_positions = {{-cursor_spacing_value, -cursor_spacing_value}, {0, -cursor_spacing_value}, {cursor_spacing_value, -cursor_spacing_value}, + {-cursor_spacing_value, 0}, {0, 0}, {cursor_spacing_value, 0}, + {-cursor_spacing_value, cursor_spacing_value}, {0, cursor_spacing_value}, {cursor_spacing_value, cursor_spacing_value}, + {-cursor_spacing_value, cursor_spacing_value*2}, {0, cursor_spacing_value*2}, {cursor_spacing_value, cursor_spacing_value*2}}, + cursor = Def.ActorFrame { + -- Move whole container + MoveCommand=function(self, param) + self:stoptweening() + self:decelerate(0.15) + self:xy(param[1], param[2]) + if param[3] then + self:z(param[3]) + end + end, + -- + LoadActor( THEME:GetPathG("_frame", "1D"), + { 2/18, 14/18, 2/18 }, + LoadActor(THEME:GetPathB("_frame", "cursors/rounded fill")) + ) .. { + OnCommand=cmd(diffuse,PlayerDarkColor(PLAYER_1)), + FitCommand=function(self, param) + self:playcommand("SetSize",{ Width=param:GetWidth()+cursor_width_padding, tween=cmd(decelerate,0.125)}) + end, + }, + LoadActor( THEME:GetPathG("_frame", "1D"), + { 2/18, 14/18, 2/18 }, + LoadActor(THEME:GetPathB("_frame", "cursors/rounded gloss")) + ) .. { + OnCommand=cmd(diffuse,PlayerColor(PLAYER_1)), + FitCommand=function(self, param) + self:playcommand("SetSize",{ Width=param:GetWidth()+cursor_width_padding, tween=cmd(decelerate,0.125)}) + end, + } + }, + cursor_draw= "first", + prompt = LoadFont("Common Bold") .. { + Name="prompt", + InitCommand=cmd(xy,0,-96); + OnCommand=cmd(shadowlength,1;skewx,-0.125;diffusebottomedge,color("#DDDDDD");strokecolor,Color.Outline); + SetCommand= function(self, params) + self:settext(params[1]) + end + }, + LoadActor(THEME:GetPathB("_frame","3x3"),"rounded black", 128, 192) .. { + InitCommand=cmd(xy, 0, -20) + } +} + +local function item_value_to_text(item, value) + if item.item_type == "bool" then + if value then + value= THEME:GetString("ScreenOptionsCustomizeProfile", item.true_text) + else + value= THEME:GetString("ScreenOptionsCustomizeProfile", item.false_text) + end + end + return value +end + +local menu_items= { + {name= "weight", get= "GetWeightPounds", set= "SetWeightPounds", + item_type= "number", auto_done= 100}, + {name= "voomax", get= "GetVoomax", set= "SetVoomax", item_type= "number", + auto_done= 10}, + {name= "birth_year", get= "GetBirthYear", set= "SetBirthYear", + item_type= "number", auto_done= 1000}, + {name= "calorie_calc", get= "GetIgnoreStepCountCalories", + set= "SetIgnoreStepCountCalories", item_type= "bool", + true_text= "use_heart", false_text= "use_steps"}, + {name= "gender", get= "GetIsMale", set= "SetIsMale", item_type= "bool", + true_text= "male", false_text= "female"}, + {name= "exit", item_type= "exit"} +} + +local menu_cursor +local menu_pos= 1 +local menu_start= 72 +local menu_x= 32 +local value_x= 300 +local fader +local cursor_on_menu= true +local menu_item_actors= {} +local menu_values= {} + +local function fade_actor_to(actor, alf) + actor:stoptweening() + actor:linear(.2) + actor:diffusealpha(alf) +end + +local function update_menu_cursor() + local item= menu_item_actors[menu_pos] + menu_cursor:playcommand("Move", {item:GetX(), item:GetY()}) + menu_cursor:playcommand("Fit", item) +end + +local function input(event) + local pn= event.PlayerNumber + if not pn then return false end + if event.type == "InputEventType_Release" then return false end + local button= event.GameButton + if cursor_on_menu then + if button == "Start" then + local item= menu_items[menu_pos] + if item.item_type == "bool" then + local value= not Profile[item.get](profile) + menu_values[menu_pos]:settext(item_value_to_text(item, value)) + Profile[item.set](profile, value) + elseif item.item_type == "number" then + fade_actor_to(fader, .8) + fade_actor_to(number_entry.container, 1) + number_entry.value= Profile[item.get](profile) + number_entry.value_actor:playcommand("Set", {number_entry.value}) + number_entry.auto_done_value= item.auto_done + number_entry.max_value= item.max + number_entry:update_cursor(number_entry.cursor_start) + number_entry.prompt_actor:playcommand( + "Set", {THEME:GetString("ScreenOptionsCustomizeProfile", item.name)}) + cursor_on_menu= false + elseif item.item_type == "exit" then + SCREENMAN:GetTopScreen():StartTransitioningScreen("SM_GoToNextScreen") + SOUND:PlayOnce(THEME:GetPathS("Common", "Start")) + end + else + if button == "MenuLeft" or button == "MenuUp" then + if menu_pos > 1 then menu_pos= menu_pos - 1 end + update_menu_cursor() + elseif button == "MenuRight" or button == "MenuDown" then + if menu_pos < #menu_items then menu_pos= menu_pos + 1 end + update_menu_cursor() + end + end + else + local done= number_entry:handle_input(button) + if done then + local item= menu_items[menu_pos] + Profile[item.set](profile, number_entry.value) + menu_values[menu_pos]:settext(item_value_to_text(item, number_entry.value)) + fade_actor_to(fader, 0) + fade_actor_to(number_entry.container, 0) + cursor_on_menu= true + end + end +end + +local args= { + Def.Actor{ + OnCommand= function(self) + update_menu_cursor() + SCREENMAN:GetTopScreen():AddInputCallback(input) + end + }, + Def.ActorFrame { + Name= "menu_cursor", InitCommand= function(self) + menu_cursor= self + end, + -- Move whole container + MoveCommand=function(self, param) + self:stoptweening() + self:decelerate(0.15) + self:xy(param[1], param[2]) + if param[3] then + self:z(param[3]) + end + end, + FitCommand= function(self, param) + self:addx(param:GetWidth()/2) + end, + -- + LoadActor( THEME:GetPathG("_frame", "1D"), + { 2/18, 14/18, 2/18 }, + LoadActor(THEME:GetPathB("_frame", "cursors/rounded fill")) + ) .. { + OnCommand=cmd(diffuse,PlayerDarkColor(PLAYER_1)), + FitCommand=function(self, param) + self:playcommand("SetSize",{ Width=param:GetWidth()+cursor_width_padding, tween=cmd(decelerate,0.125)}) + end, + }, + LoadActor( THEME:GetPathG("_frame", "1D"), + { 2/18, 14/18, 2/18 }, + LoadActor(THEME:GetPathB("_frame", "cursors/rounded gloss")) + ) .. { + OnCommand=cmd(diffuse,PlayerColor(PLAYER_1)), + FitCommand=function(self, param) + self:playcommand("SetSize",{ Width=param:GetWidth()+cursor_width_padding, tween=cmd(decelerate,0.125)}) + end, + } + }, +} + +for i, item in ipairs(menu_items) do + local item_y= menu_start + ((i-1) * 24) + args[#args+1]= Def.BitmapText{ + Name= "menu_" .. item.name, Font= "Common Normal", + Text= THEME:GetString("ScreenOptionsCustomizeProfile", item.name), + InitCommand= function(self) + menu_item_actors[i]= self + self:xy(menu_x, item_y) + self:diffuse(Color.White) + self:horizalign(left) + end + } + if item.get then + local value_text= item_value_to_text(item, Profile[item.get](profile)) + args[#args+1]= Def.BitmapText{ + Name= "value_" .. item.name, Font= "Common Normal", + Text= value_text, + InitCommand= function(self) + menu_values[i]= self + self:xy(value_x, menu_start + ((i-1) * 24)) + self:diffuse(Color.White) + self:horizalign(left) + end + } + end +end + +args[#args+1]= Def.Quad{ + Name= "fader", InitCommand= function(self) + fader= self + self:setsize(270, #menu_items * 24) + self:horizalign(left) + self:vertalign(top) + self:xy(menu_x-10, menu_start-12) + self:diffuse(Color.Black) + self:diffusealpha(0) + end +} + +args[#args+1]= number_entry:create_actors() + +return Def.ActorFrame(args) diff --git a/Themes/default/BGAnimations/_frame cursors/rounded fill.png b/Themes/default/BGAnimations/_frame cursors/rounded fill.png new file mode 100644 index 0000000000..37cbb834a5 Binary files /dev/null and b/Themes/default/BGAnimations/_frame cursors/rounded fill.png differ diff --git a/Themes/default/BGAnimations/_frame cursors/rounded gloss.png b/Themes/default/BGAnimations/_frame cursors/rounded gloss.png new file mode 100644 index 0000000000..1757e4c9ea Binary files /dev/null and b/Themes/default/BGAnimations/_frame cursors/rounded gloss.png differ diff --git a/Themes/default/BGAnimations/_frame cursors/rounded white.png b/Themes/default/BGAnimations/_frame cursors/rounded white.png new file mode 100644 index 0000000000..0c75dcb089 Binary files /dev/null and b/Themes/default/BGAnimations/_frame cursors/rounded white.png differ diff --git a/Themes/default/Graphics/ScreenWithMenuElements header/default.lua b/Themes/default/Graphics/ScreenWithMenuElements header/default.lua index 73675ace04..30b1875124 100644 --- a/Themes/default/Graphics/ScreenWithMenuElements header/default.lua +++ b/Themes/default/Graphics/ScreenWithMenuElements header/default.lua @@ -36,8 +36,8 @@ t[#t+1] = LoadFont("Common Bold") .. { t[#t+1] = Def.Quad { Name="Underline"; - InitCommand=cmd(x,-SCREEN_CENTER_X+24;y,36;horizalign,left); - OnCommand=cmd(visible,IsVisible();diffuse,color("#ffd400");shadowlength,1;linear,0.25;zoomtowidth,192;faderight,0.5); + InitCommand=cmd(x,-SCREEN_CENTER_X+24-4;y,36;horizalign,left); + OnCommand=cmd(diffuse,color("#ffd400");shadowlength,2;shadowcolor,BoostColor(color("#ffd40077"),0.25);linear,0.25;zoomtowidth,192;fadeleft,8/192;faderight,0.5); }; t[#t+1] = LoadFont("Common Bold") .. { @@ -54,5 +54,4 @@ t.BeginCommand=function(self) self:SetUpdateFunction( Update ); end - return t diff --git a/Themes/default/Languages/en.ini b/Themes/default/Languages/en.ini index ed9b7f2368..59926aa903 100644 --- a/Themes/default/Languages/en.ini +++ b/Themes/default/Languages/en.ini @@ -85,6 +85,11 @@ HelpText= [ScreenGameplay] HelpText= +[ScreenHeartEntry] +Enter Heart Rate=Enter Heart Rate +Song Length=Song Length +Heart Rate=Heart Rate + [ScreenEvaluation] HelpText=&BACK; Exit &START; Move On &MENULEFT;+&MENURIGHT; or &SELECT; Snapshot LifeDifficulty=Life Difficulty: %s diff --git a/Themes/default/metrics.ini b/Themes/default/metrics.ini index 4a83a33650..7bc8be258a 100644 --- a/Themes/default/metrics.ini +++ b/Themes/default/metrics.ini @@ -1628,6 +1628,11 @@ ActiveAttackListP2OffCommand= [ScreenGameplayShared] +[ScreenHeartEntry] +HeartEntryEnabled=true +ShowHeader=true +ShowFooter=true + [ScreenEvaluation] Class="ScreenEvaluation" Fallback="ScreenWithMenuElements" diff --git a/src/GameState.cpp b/src/GameState.cpp index 858c3b8596..d16009ff5b 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -2439,6 +2439,7 @@ public: p->m_Position.PushSelf(L); return 1; } + DEFINE_METHOD( GetLastGameplayDuration, m_DanceDuration ) DEFINE_METHOD( GetGameplayLeadIn, m_bGameplayLeadIn ) DEFINE_METHOD( GetCoins, m_iCoins ) DEFINE_METHOD( IsSideJoined, m_bSideIsJoined[Enum::Check(L, 1)] ) @@ -2848,6 +2849,7 @@ public: ADD_METHOD( GetSongFreeze ); ADD_METHOD( GetSongDelay );*/ ADD_METHOD( GetSongPosition ); + ADD_METHOD( GetLastGameplayDuration ); ADD_METHOD( GetGameplayLeadIn ); ADD_METHOD( GetCoins ); ADD_METHOD( IsSideJoined ); diff --git a/src/GameState.h b/src/GameState.h index 9265d9fd71..126d2c4ea2 100644 --- a/src/GameState.h +++ b/src/GameState.h @@ -271,6 +271,10 @@ public: float m_fLastHasteUpdateMusicSeconds; float m_fAccumulatedHasteSeconds; + // used by themes that support heart rate entry. + RageTimer m_DanceStartTime; + float m_DanceDuration; + // Random Attacks & Attack Mines vector m_RandomAttacks; diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 80acf2819c..5b6c7ff8fa 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -2509,6 +2509,8 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) else SOUND->PlayOnceFromAnnouncer( "gameplay here we go normal" ); + GAMESTATE->m_DanceStartTime.Touch(); + m_Go.StartTransitioning( SM_None ); GAMESTATE->m_bGameplayLeadIn.Set( false ); m_DancingState = STATE_DANCING; // STATE CHANGE! Now the user is allowed to press Back @@ -2558,6 +2560,7 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) } else if( SM == SM_LeaveGameplay ) { + GAMESTATE->m_DanceDuration= GAMESTATE->m_DanceStartTime.Ago(); // update dancing characters for win / lose DancingCharacters *pDancers = NULL; if( m_pSongBackground ) diff --git a/src/ScreenOptionsManageProfiles.cpp b/src/ScreenOptionsManageProfiles.cpp index 7d312554c2..dae1d916e0 100644 --- a/src/ScreenOptionsManageProfiles.cpp +++ b/src/ScreenOptionsManageProfiles.cpp @@ -113,7 +113,7 @@ void ScreenOptionsManageProfiles::BeginScreen() Profile *pProfile = PROFILEMAN->GetLocalProfile( *s ); ASSERT( pProfile != NULL ); - RString sCommand = ssprintf( "gamecommand;screen,ScreenOptionsEditProfile;profileid,%s;name,dummy", s->c_str() ); + RString sCommand = ssprintf( "gamecommand;screen,ScreenOptionsCustomizeProfile;profileid,%s;name,dummy", s->c_str() ); OptionRowHandler *pHand = OptionRowHandlerUtil::Make( ParseCommands(sCommand) ); OptionRowDefinition &def = pHand->m_Def; def.m_layoutType = LAYOUT_SHOW_ALL_IN_ROW; @@ -380,7 +380,7 @@ void ScreenOptionsManageProfiles::ProcessMenuStart( const InputEventPlus & ) } else { - //ADD_ACTION( ProfileAction_Edit ); + ADD_ACTION( ProfileAction_Edit ); ADD_ACTION( ProfileAction_Rename ); ADD_ACTION( ProfileAction_Delete ); } diff --git a/src/ThemeManager.cpp b/src/ThemeManager.cpp index 739bb93e41..914ac0f143 100644 --- a/src/ThemeManager.cpp +++ b/src/ThemeManager.cpp @@ -1056,6 +1056,7 @@ void ThemeManager::PushMetric( Lua *L, const RString &sMetricsGroup, const RStri if(sMetricsGroup == "" || sValueName == "") { LuaHelpers::ReportScriptError("PushMetric: Attempted to fetch metric with empty group name or empty value name."); + lua_pushnil(L); return; } RString sValue = GetMetricRaw( g_pLoadedThemeData->iniMetrics, sMetricsGroup, sValueName );