KKI LABS // PUBLIC RELEASE

Banner Wheel Exposed
by freem of KKI Labs/SSC
Draft 2
Official Submission Date: August 16, 2011
[Draft 1, plaintext edition]

Abstract

Background

In some rhythm games, the music selection screen likes to implement banners or backgrounds, depending on the game chosen. Until 2008, there were two ways to implement this in StepMania:
  1. Use ScreenEz2SelectMusic, which hasn't been updated since 3.9
  2. Hack the source code to include it yourself (Neon FM, Mungyodance)
These days, people using StepMania 3.9 have access to 3.9+, which includes an incomplete banner wheel option. The reason it's incomplete is that it doesn't allow for groups to have banners on the wheel.

However, 3.9 is not a modern version of StepMania by any means.

Conclusion

Using banners on the MusicWheel isn't as hard as you think, but you will need StepMania 5 or some source hacks (for older versions) to get a 100% functioning banner wheel.

Banner Wheel: Implementation and Analyzation

Introduction:
Before I introduce the problem, I feel I have a bit of explaining to do.

First off, this idea was revealed to work in February 2008. This guide was written in September 2008, 7 months apart. "Why the wait?", you may ask.

Well, I wanted to originally release the guide with a version of StepMania that supported the source hack mentioned in this guide. However, it's been eight months since the last SM4 release, and hypnodance is crawling along at a speed of one change every 6 months.

Secondly, some people have given other people the banner wheel code without asking me. This is especially obvious in cerbo's PIU NX theme.

If other people are using it, I don't really have any reason to hide it aside from the source code hack.

So before I begin, I must say this:
If you are using StepMania 4 CVS (last version 20080103), the group banners will not work for you. If this is an issue, then you have two choices:
  1. Move to StepMania 5 (please)
  2. Hack the source.
We now return you to your regularly scheduled release.

Problem: Get banners to show up for songs (and groups) on the MusicWheel.

Hypothesis: By using a Lua actor to replace MusicWheelItem, you can make a lot of things (including banner wheels) happen.

Implementation:
This was my first time ever messing with Lua to replace MusicWheelItem, so I didn't know what to expect.

After messing with it for a bit, I looked at Lua.xml for any possible classes I could use in here and I saw Banner. Banner has quite a few interesting functions in it, namely LoadFromSong().

However, that's only half of the story. You also need to get access to every single song that's on the wheel... and that's where it starts to get dirty, messy, and mind-blowing.

Implementation Details:
There should be a file called [YourTheme]/Graphics/MusicWheelItem Song NormalPart/default.lua, which we will edit. (In previous versions, this is [YourTheme]/Graphics/MusicWheel item/default.lua)

As mentioned above, there is a class called Banner (called in Lua files by using Def.Banner) that has a few useful functions such as LoadFromSong(). However, the problem is getting access to the songs, and this is where our friend MESSAGEMAN comes in.

Thumbing through the source code of MusicWheelItem.cpp shows us a nice block of code to work with (code from SM5):
{
	Message msg( "Set" );
	msg.SetParam( "Song", data->m_pSong );
	msg.SetParam( "Course", data->m_pCourse );
	msg.SetParam( "Index", iIndex );
	msg.SetParam( "HasFocus", bHasFocus );
	msg.SetParam( "Text", pWID->m_sText ); // "SongGroup" in SM4 alpha versions
	this->HandleMessage( msg );
}
That's fine and all if you're familiar with handling messages, but since it's likely that you're not, I'll show you how.

For every message sent, there is a way to capture it in a theme.

The formula is [x]MessageCommand, where [x] is the name of the message. Since the message above is named Set, the command you need is SetMessageCommand.

As you can see, it gives us a few parameters. If you've worked with long form command declarations before ( =function(self) ), then it's just a matter of adding one more parameter to the function.

If not, though, then here's everything you need to know for this specific instance:

SetMessageCommand=function(self,params)

I use "params", some people use "param", others may use "p", it's all up to you. You just need to know what you use so you can call the parameters.

Alright, so now that we have an idea of how to abuse the SetMessage called in MusicWheelItem, it's time to make a skeleton.
local t = Def.ActorFrame{
	Def.Banner{
		InitCommand=cmd(scaletoclipped,256,80);
		SetMessageCommand=function(self,params)
		end;
	}
}

return t;
So we have the skeleton. I've also thrown in an InitCommand that scales the banner down to 256x80. If you're not doing this with DDR banners or you want to have a different banner size, then change the two params to scaletoclipped.

From here, we need to flesh out SetMessageCommand. Look at the param list above and think of what you would need from it.

If you said params.Song, you win! From this point forward, all examples will only include SetMessageCommand unless otherwise stated.
SetMessageCommand=function(self,params)
	local song = params.Song;
end;
Now, we need to test to see if there is a song or else things will go haywire:
SetMessageCommand=function(self,params)
	local song = params.Song;
	if song then
		-- this is where we do all song-specific stuff
		self:LoadFromSong(params.Song);
	end;
end;
And if all you want to do is display the song banner, then there you have it.

Deceptively simple, but ultra-powerful. Perhaps one of the next KKI Labs guides will expose the true functionality of MusicWheelItem Song NormalPart/default.lua, but until then, you can look at moonlight (SM5) and NAKET Coder Evolution (SM4CVS) to see how to abuse it.

Of course, this is not a complete implementation, as this only deals with songs.

For courses, change your SetMessageCommand to the following:
SetMessageCommand=function(self,params)
	local song = params.Song;
	local course = params.Course;
	if song and not course then
		-- this is where we do all song-specific stuff
		self:LoadFromSong(params.Song);
	elseif course and not song then
		-- this is where we do all course-specific stuff
		self:LoadFromCourse(params.Course);
	end;
end;
Then that just leaves us with groups as one of the main things to consider. However, you really need to have a version of SM with Banner::LoadFromSongGroup() in order to even begin considering to have group banners on your wheel. You'll be editing MusicWheelItem SectionCollapsed NormalPart/default.lua and MusicWheelItem SectionExpanded NormalPart/default.lua (just MusicWheelItem section/default.lua in SM4a5), but the code remains nearly the same. Remember msg.SetParam( "Text", pWID->m_sText );?

with the skeleton:
local t = Def.ActorFrame{
	Def.Banner{
		InitCommand=cmd(scaletoclipped,256,80);
		SetMessageCommand=function(self,params)
			local group = params.Text;
			if group then
				self:LoadFromSongGroup(group);
			else
				-- call fallback
				self:Load( THEME:GetPathG("Common fallback","banner") );
			end;
		end;
	}
}

return t;
From here, the sky's the limit, as you can implement other things on top of SetMessageCommand, since you get access to the raw Song and Course. Any commands you can run on those, you can run in here.