Merge branch 'master' of github.com:stepmania/stepmania
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
-- This example describes everything you need to do in your theme to allow the
|
||||
-- player to enter their heart rate for calorie calculation.
|
||||
|
||||
-- First, the metrics you'll need to add to metrics.ini.
|
||||
-- [ScreenHeartEntry]
|
||||
-- # The HeartEntryEnabled metric must be set to true so the branching logic
|
||||
-- # will know that your theme supports the screen.
|
||||
-- HeartEntryEnabled=true
|
||||
-- # ScreenHeartEntry uses ScreenWithMenuElements as its fallback, so the
|
||||
-- # metrics in the ScreenWithMenuElements section will also apply to it.
|
||||
|
||||
-- Now for the body of the screen. The things discussed here will have to go
|
||||
-- in BGAnimations/ScreenHeartEntry overlay.lua
|
||||
|
||||
-- The positions the numpads for entering the heart rate will be placed at.
|
||||
local heart_xs= {
|
||||
[PLAYER_1]= SCREEN_CENTER_X * 0.625,
|
||||
[PLAYER_2]= SCREEN_CENTER_X * 1.375,
|
||||
}
|
||||
|
||||
-- heart_entries is a table that will be used to store the numpads that the
|
||||
-- players will use.
|
||||
local heart_entries= {}
|
||||
-- Note that a numpad is only created for a player if that player is enabled
|
||||
-- and they have the flag set in their profile to enable heart rate entry.
|
||||
for i, pn in ipairs(GAMESTATE:GetEnabledPlayers()) do
|
||||
local profile= PROFILEMAN:GetProfile(pn)
|
||||
if profile and profile:GetIgnoreStepCountCalories() then
|
||||
-- This is probably the part you'll want to configure the most, the
|
||||
-- numpads. _fallback/Scripts/04 NumPadEntry.lua discusses configuring a
|
||||
-- numpad in detail.
|
||||
heart_entries[pn]= new_numpad_entry{
|
||||
Name= pn .. "_heart_entry",
|
||||
InitCommand= cmd(xy, heart_xs[pn], SCREEN_CENTER_Y+48),
|
||||
-- Settings for value are optional, but you will probably want to
|
||||
-- change them, so they are provided in this example.
|
||||
-- If a simple colored BitmapText isn't what you want for displaying
|
||||
-- the value being entered, read _fallback/Scripts/04 NumPadEntry.lua.
|
||||
value_pos= {0, -48},
|
||||
value_font= "Common Normal",
|
||||
value_color= PlayerColor(pn),
|
||||
-- Optional prompt settings.
|
||||
prompt_pos= {0, -72},
|
||||
prompt_font= "Common Normal",
|
||||
prompt_color= Color.White,
|
||||
prompt_text= THEME:GetString("ScreenHeartEntry", "Heart Rate"),
|
||||
-- Optional button settings.
|
||||
button_positions= {
|
||||
{-30, -24}, {0, -24}, {30, -24},
|
||||
{-30, 0}, {0, 0}, {30, 0},
|
||||
{-30, 24}, {0, 24}, {30, 24},
|
||||
{-30, 48}, {0, 48}, {30, 48}
|
||||
},
|
||||
button_font= "Common Normal",
|
||||
button_color= Color.White,
|
||||
-- Cursor settings. You probably want something more than a quad for
|
||||
-- the cursor, so this example includes the command the cursor needs
|
||||
-- to support to do its job.
|
||||
cursor= Def.Quad{
|
||||
InitCommand= function(self)
|
||||
self:SetWidth(16)
|
||||
self:SetHeight(28)
|
||||
self:diffuse(PlayerDarkColor(pn))
|
||||
end,
|
||||
-- MoveCommand is used to move the cursor to a button.
|
||||
MoveCommand= function(self, param)
|
||||
-- param contains the position to move to.
|
||||
self:stoptweening()
|
||||
self:decelerate(0.15)
|
||||
self:xy(param[1], param[2])
|
||||
end,
|
||||
-- FitCommand is used to change the size of the cursor to fit the
|
||||
-- button after it has been moved.
|
||||
FitCommand= function(self, param)
|
||||
-- param is the actor for the button.
|
||||
-- Note that this does not use stoptweening or finishtweening.
|
||||
-- This is because it is executed with playcommand immediately
|
||||
-- after MoveCommand, and thus the state changes it makes combine
|
||||
-- with the actor state at the end of MoveCommand.
|
||||
self:SetWidth(param:GetWidth())
|
||||
end
|
||||
},
|
||||
-- cursor_draw controls whether the cursor is above or below the
|
||||
-- buttons.
|
||||
cursor_draw= "first", -- stick it under the buttons.
|
||||
-- Don't let the player enter a silly value.
|
||||
max_value= 300,
|
||||
-- Automatically move the cursor to the done button when the player has
|
||||
-- entered three digits.
|
||||
auto_done_value= 100,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- This function is the input callback. It handles button presses from the
|
||||
-- player, passing them to the numpad so the player can enter their heart
|
||||
-- rate, and detecting when the player is done.
|
||||
-- See the AddInputCallback function for the Screen class in Lua.xml for a
|
||||
-- full description of the event argument.
|
||||
local function input(event)
|
||||
local pn= event.PlayerNumber
|
||||
-- If the PlayerNumber isn't set, the button isn't mapped. Ignore it.
|
||||
if not pn then return end
|
||||
-- If it's a release, ignore it.
|
||||
if event.type == "InputEventType_Release" then return end
|
||||
-- If the player doesn't have a heart_entry, ignore it.
|
||||
if not heart_entries[pn] then return end
|
||||
-- Pass the input to the heart_entry for the player. It will handle moving
|
||||
-- the cursor. If it returns true, the player is done.
|
||||
local done= heart_entries[pn]:handle_input(event.GameButton)
|
||||
if done then
|
||||
-- Play a sound for the player so they know the value was entered.
|
||||
SOUND:PlayOnce(THEME:GetPathS("Common", "Start"))
|
||||
-- Check whether all players that need to enter their heart rate are done.
|
||||
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
|
||||
-- If the players are done, add the calories to their profiles.
|
||||
for pn, entry in pairs(heart_entries) do
|
||||
local profile= PROFILEMAN:GetProfile(pn)
|
||||
if profile and profile:GetIgnoreStepCountCalories() then
|
||||
-- calories is the value you might want to store off in a global
|
||||
-- variable to display on evaluation.
|
||||
local calories= profile:CalculateCaloriesFromHeartRate(
|
||||
entry.value, GAMESTATE:GetLastGameplayDuration())
|
||||
profile:AddCaloriesToDailyTotal(calories)
|
||||
end
|
||||
end
|
||||
-- Since all players are done, transition to the next screen.
|
||||
SCREENMAN:GetTopScreen():StartTransitioningScreen("SM_GoToNextScreen")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- A simple timer that the player can watch while counting their pulse.
|
||||
-- The menu timer isn't used because it has undesired side effects.
|
||||
-- timer_text will be set to the BitmapText used to display the time. This
|
||||
-- way, the BitmapText doesn't have to be fetched with GetChild every frame.
|
||||
local timer_text
|
||||
-- This is an update function for an ActorFrame. It will run every frame.
|
||||
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
|
||||
|
||||
-- args contains the actors that will be in the ActorFrame for this screen.
|
||||
local args= {
|
||||
Def.ActorFrame{
|
||||
Name= "timer",
|
||||
InitCommand= function(self)
|
||||
-- Set the effectperiod so the timer can show an elapsed time.
|
||||
self:effectperiod(2^16)
|
||||
-- Set timer_text so GetChild doesn't need to be called every frame.
|
||||
timer_text= self:GetChild("timer_text")
|
||||
-- Set the update function so the timer will be updated.
|
||||
self:SetUpdateFunction(timer_update)
|
||||
end,
|
||||
OnCommand= function(self)
|
||||
-- Add the input callback so input will be handled.
|
||||
SCREENMAN:GetTopScreen():AddInputCallback(input)
|
||||
end,
|
||||
-- The BitmapText used to display the elapsed time.
|
||||
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),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
-- The actors for the heart_entries are added in a loop so that only the ones
|
||||
-- that will be used will exist.
|
||||
for pn, entry in pairs(heart_entries) do
|
||||
args[#args+1]= entry:create_actors()
|
||||
end
|
||||
|
||||
return Def.ActorFrame(args)
|
||||
@@ -6,6 +6,7 @@ The first system simply uses the weight of the player and adds a small amount fo
|
||||
The second system uses age, gender, weight, heart rate, and song duration to calculate the amount used during a song. This requires a theme to support it, and the relevant parts of Editable.ini to be set for the profile.
|
||||
|
||||
Editable.ini fields:
|
||||
The default theme provides a profile editing screen for setting these fields in Stepmania without needing to edit Editable.ini by hand.
|
||||
BirthYear, -- defaults to 1995
|
||||
IgnoreStepCountCalories, -- Must be set to 1 to use the second system.
|
||||
IsMale, -- Defaults to 1. Set to 0 for females.
|
||||
@@ -29,22 +30,9 @@ CalculateCaloriesFromHeartRate()
|
||||
|
||||
Intended usage:
|
||||
A theme that supports heart rate calorie calculation should take the following steps:
|
||||
1. Create a new screen that the player will be able to use to enter their heart rate. It shall be called "ScreenHeartEntry" in this list, though a theme can name it anything.
|
||||
2. During ScreenGameplay, track the amount of time that the player is hitting steps for. This duration must be in seconds.
|
||||
3. When proceeding from ScreenGameplay:
|
||||
3a. Check whether GetIgnoreStepCountCalories returns true for either player's profile.
|
||||
3b. If it returns true for either player, go to ScreenHeartEntry instead of ScreenEvaluationNormal.
|
||||
3c. This means changing the NextScreen metric for ScreenGameplay to use a function that checks the profiles and returns the correct screen.
|
||||
4. On ScreenHeartEntry:
|
||||
4a. Provide a timer that the players can watch while measuring their pulse.
|
||||
4b. Provide a way for the players to enter their heart rate in beats per minute.
|
||||
4c. When a player enters their heart rate, use Profile:CalculateCaloriesFromHeartRate().
|
||||
4d. Do not add calories to a profile if Profile:IgnoreStepCountCalories() returns false for that profile.
|
||||
4e. Pass the heart rate and the duration from ScreenGameplay to Profile:CalculateCaloriesFromHeartRate() and store its return value.
|
||||
4f. Pass the return value of Profile:CalculateCaloriesFromHeartRate() to Profile:AddCaloriesToDailyTotal() to add them to the total for that player's profile.
|
||||
4g. When all active players have finished entering their heart rate, proceed to ScreenEvaluationNormal.
|
||||
5. This concludes the special steps required for theme support of heart rate calorie calculation.
|
||||
1. Create ScreenHeartEntry as described in Themerdocs/Examples/Example_Screens/ScreenHeartEntry.lua. _fallback's branching logic will take care of checking whether the screen should be used.
|
||||
|
||||
Side notes:
|
||||
CalculateCaloriesFromHeartRate and AddCaloriesToDailyTotal are separate functions so that a theme can display or use the calorie amount in other ways easily.
|
||||
If IgnoreStepCountCalories is true for a profile, but the theme in use does not provide support using the steps listed above, that profile will not accumulate calories.
|
||||
Note that calories will not accumulate during gameplay for a profile that has IgnoreStepCountCalories set to true. PlayerStageStats:GetCaloriesBurned() will also return 0 for that player. You'll have to store the value returned by CalculateCaloriesFromHeartRate in a global variable if you want to display it later on ScreenEvaluation.
|
||||
|
||||
@@ -183,6 +183,8 @@
|
||||
-- -- is a table of x, y, and optional z. Unused if you pass in a custom
|
||||
-- -- value actor.
|
||||
-- value_pos= {0, -48},
|
||||
-- -- Optional. The font to use for the default value actor.
|
||||
-- value_font= "Common Normal",
|
||||
-- -- Optional. The color for the default value text. Unused if you pass
|
||||
-- -- in a custom value actor. Default is White.
|
||||
-- value_color= Color.White,
|
||||
|
||||
Reference in New Issue
Block a user