Forums » Themes » Making a theme from scratch

I'm trying to make a theme for SM5 (beta 1a) from scratch. So far, the documentation has been almost useless.
What I'd like to see in the documentation is this:
0. Documentation structure chart. This would detail where to find each of the pieces listed below. Ideally, the documentation should not rely on some internet website, because page load times obstruct viewing. Additionally, any web-based documentation is more likely to be out of sync with the local copy of stepmania.
1. A structure flowchart. This would detail the dependency structure of a theme. Which parts are optional, which are required, which are semi-required in that they are loaded from _fallback if not present. This should also detail what information needs to be in files for the theme to load correctly.
2. API-style documentation for metrics. This should detail what sections must exist, what sections are optional, and what is required and optional inside sections. If section names and elements can be arbitrarily chosen by the themer, the correct way of doing so should be described. For sections and section elements that can be done in lua instead, the method for doing so should be detailed. Things that should never be touched by common themers and are handled by _fallback should be left out of this, or separated in some way.
3. Details of how Docs/Luadoc/Lua.xml should be used. The classes and their members are already detailed, and the file is generated at compile time, which is good. But there isn't an explanation of where in a theme each part belongs.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
Is there an explanation of what a command (such as InitCommand) is, and a list of what commands exist?

Is the lua function addcommand supposed to be used to add commands that are identical in ability to the default commands?

If so, how are these commands supposed to be triggered?
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
InitCommand, BeginCommand, OnCommand, OffCommand are the ones you use most of the time, and when you want to trigger another command you can use playcommand(name) or queuecommand(name). Use queuecommand unless you know for a fact you need the other one.

Various messages are documented here: https://github.com/stepmania/stepmania/blob/master/Docs/Themerdocs/moremsg.txt

Also here: https://github.com/stepmania/stepmania/blob/master/Docs/Themerdocs/ScreenMessages.txt I think these trigger commands like 1000ComboMessageCommand (etc).
Reply
Other commands I can think of include:

[ulist]
[*]CancelCommand (run when backing out of a screen),
[*]FirstUpdateCommand (run on the screen's first update; after BeginCommand? have not looked into this, as I haven't really used it ever)
[*]StartTransitioningCommand (typically used for things like Toasty or whatnot)
[*]EndCommand (another command I haven't used ever; runs on Screen::EndScreen(), so possibly after OffCommand?? not sure)
[/ulist]

And here some specific class commands (but not all of them; I searched for
PlayCommand( "
in the source directory, if you want to find the rest). Some commands may not have proper explanations since I haven't used them all, or use them super sparingly.

Background (Background Animations)
[ulist]
[*]LoseFocusCommand (BGAnim transitioning out?)
[*]GainFocusCommand (BGAnim transitioning in?)
[*]FadeCommand (BGAnim fading to real brightness)
[/ulist]

DifficultyList cursor
[ulist]
[*]ChangeCommand (played on changing difficulties)
[/ulist]

FadingBanner
[ulist]
[*]FadeFromCachedCommand (played on low-res banner -> high-res banner change)
[*]FadeOffCommand (played when banner finishes fading?)
[*]ResetFadeCommand (played just before the banner changes)
[*]RouletteCommand (played on changing to Roulette wheelitem)
[*]RandomCommand (played on changing to Random wheelitem)
[/ulist]

GhostArrowRow
[ulist]
[*]HoldingOffCommand
[*]RollOffCommand
[*]HoldingOnCommand
[*]RollOnCommand
[/ulist]

HoldJudgment
These commands are in the metrics under the [HoldJudgment] section:
[ulist]
[*]HeldCommand (played on successful hold; "OK")
[*]LetGoCommand (played on failed hold; "NG")
[/ulist]

LyricDisplay
[ulist]
[*]ChangedCommand (played when the lyric text changes)
[/ulist]

NoteDisplay
[ulist]
[*]UnsetAttackCommand (played when an attack finishes up)
[/ulist]

OptionsList
[ulist]
[*]SetTwoRowsCommand (used by underline when the OptionsList shows two rows)
[*]SetOneRowCommand (used by underline when the OptionsList shows one row)
[*]PositionTwoRowsCommand (used by cursor when the OptionsList shows two rows)
[*]PositionOneRowCommand (used by cursor when the OptionsList shows one row)
[*]TweenOnCommand (called on opening the OptionsList)
[*]TweenOffCommand (called at screen beginning and on OptionsList close)
[*]ResetCommand (reset optionslist)
[*]TweenOutForward
[*]TweenOutBackward
[*]TweenInForward
[*]TweenInBackward
[/ulist]

Reply
CodeDetector is completely undocumented. I'm looking for a way to make it call an arbitrary lua function.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
CodeDetector broadcasts messages, and doesn't call functions itself. When a code is triggered you get CodeMessageCommand(self, params) and params should have player and name in it.
Reply
From that, it sounded like I should go to http://kki.ajworld.net/wiki/Tutorials:Messages to learn how to catch those messages.
From that page, it looked like I should make something like this:
In metrics.ini:
[CodeDetector]
PlaceHolderTest="Left,Down,Right,Down,Left"
In ScreenSelectMusic background.lua (inside the ActorFrame definition):
ssm_code_handler= LoadActor("ssm_code_handler"),
In ssm_code_handler.lua:
return Def.ActorFrame{
PlaceHolderTestMessageCommand= function() Trace("PlaceHolderTestMessageCommand recieve in SSMch.lua.") end,
}

But with those pieces in place, nothing happens when I put in the code I defined.
I took the step of clearing all of the codes that _fallback sets to avoid collisions, (and unwanted codes) so that's not the problem.
Additionally, nowhere in _fallback is there something that looks like it catches messages from CodeDetector in that way. In fact, a cursory examination of src/CodeDetector.cpp shows that the CodeDetector has a specific set of code names that can be set, and each calls a specific function in that file. Thus, CodeDetector is not suitable for a theme that wants to pick up arbitrary step codes and call arbitrary lua functions.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
I spent some time examining the source code last night, here's what I learned.

Screen::Init calls InputQueueCodeSet::Load to load input codes for the screen. InputQueueCodeSet::Load gets the metric "CodeNames" from the screen's section to get the string of code names. This string is split along the comma delimiters, each part being the name of a code. InputQueueCodeSet::Load prepends "Code" to each name to get the metric data for that code. Thus "CodeNames="Foo,Bar,Baz"" should cause InputQueueCodeSet::Load to load the codes set by "CodeFoo=", "CodeBar=", and "CodeBaz=".
A code should not be named "Names" because it would be set by the metric "CodeNames", and that metric is already used to store the names of the codes.
A code name should not end in "Command" because it would also be registered as a command for that screen, which would be nonsensical.

Screen::Input passes input to InputQueueCodeSet::InputMessage to check for a special code before any other handling. If a code is matched, Screen::HandleMessage is called with a message named "Code". Screen::HandleMessage is actually Actor::PlayCommandNoRecurse. Actor::PlayCommandNoRecurse searches a map of commands for one named "Code" and runs that command.
From this, we can see that the message from entering a code defined through InputQueueCodeSet is not broadcast, does not pass through the message manager, and the command for that message must be in the screen that is receiving the input.

Actor::LoadFromNode checks each attribute to see if the name ends in "Command". If the name ends in "Command", "Command" is truncated from the name and the remaining name is passed on to Actor::AddCommand. Actor::AddCommand checks to see if the name ends in "Message. If the name ends in "Message", "Message is truncated from the name, and the actor subscribes to the message. The remaining name is added as a command. From this chain, we see that "FooMessageCommand" and "FooCommand" both create a command named "Foo" for the actor.
For the case of the command for an input code, it is useless to name it "CodeMessageCommand" rather than "CodeCommand", because the message does not pass through the message manager, and is run directly as a command.


So far, from these parts, it appears that the correct method is this:
In metrics.ini:

[ScreenSelectMusic]
CodeNames="PlaceHolderTest"
CodePlaceHolderTest="Left,Down,Right,Down,Left"
CodeMessageCommand=%function(self, param) Trace("CodeMessageCommand received.") end


But that didn't when I tried it, so I've done something wrong along the way.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply