From 9c6ca1381b2fc4a77de9853e4585baaa21afe6e2 Mon Sep 17 00:00:00 2001 From: Josh Allen Date: Sun, 27 Mar 2005 03:56:10 +0000 Subject: [PATCH] Very basic and generic selection wheel using code from existing MusicWheel and MusicWheelItems. MusicWheel and MusicWheelItems will abstract from this eventually. --- stepmania/src/WheelBase.cpp | 742 ++++++++++++++++++++++++++++++++ stepmania/src/WheelBase.h | 155 +++++++ stepmania/src/WheelItemBase.cpp | 173 ++++++++ stepmania/src/WheelItemBase.h | 87 ++++ 4 files changed, 1157 insertions(+) create mode 100644 stepmania/src/WheelBase.cpp create mode 100644 stepmania/src/WheelBase.h create mode 100644 stepmania/src/WheelItemBase.cpp create mode 100644 stepmania/src/WheelItemBase.h diff --git a/stepmania/src/WheelBase.cpp b/stepmania/src/WheelBase.cpp new file mode 100644 index 0000000000..67eb248729 --- /dev/null +++ b/stepmania/src/WheelBase.cpp @@ -0,0 +1,742 @@ +#include "global.h" +#include "WheelBase.h" +#include "RageUtil.h" +#include "SongManager.h" +#include "GameManager.h" +#include "PrefsManager.h" +#include "ScreenManager.h" // for sending SM_PlayMusicSample +#include "RageLog.h" +#include "GameConstantsAndTypes.h" +#include "GameState.h" +#include "RageMath.h" +#include "ThemeManager.h" +#include "song.h" +#include "Course.h" +#include "RageDisplay.h" +#include "RageTextureManager.h" +#include "Banner.h" +#include "Steps.h" +#include "UnlockManager.h" +#include "GameCommand.h" +#include "ActorUtil.h" +#include "SongUtil.h" +#include "CourseUtil.h" +#include "Foreach.h" +#include "Style.h" +#include "ThemeMetric.h" +#include "PlayerState.h" +#include "ScreenDimensions.h" + + +#define NUM_WHEEL_ITEMS ((int)ceil(NUM_WHEEL_ITEMS_TO_DRAW+2)) + +// leaving this one under ScreenSelectMusic because that is the only place it takes effect anyway. +//ThemeMetric DEFAULT_SORT ("ScreenSelectMusic","DefaultSort"); + +//static CString SECTION_COLORS_NAME( size_t i ) { return ssprintf("SectionColor%d",int(i+1)); } +static CString CHOICE_NAME( CString s ) { return ssprintf("Choice%s",s.c_str()); } + +const int MAX_WHEEL_SOUND_SPEED = 15; + +template +void CircularShift( vector &v, int dist ) +{ + for( int i = abs( dist ); i>0; i-- ) + { + if( dist > 0 ) + { + T t = v[0]; + v.erase( v.begin() ); + v.push_back( t ); + } + else + { + T t = v.back(); + v.erase( v.end()-1 ); + v.insert( v.begin(), t ); + } + } +} + +WheelBase::WheelBase() +{ +} + +WheelBase::~WheelBase() +{ + FOREACH( WheelItemBase*, m_WheelBaseItems, i ) + SAFE_DELETE( *i ); + m_WheelBaseItems.clear(); + FOREACH( WheelItemBaseData*, m_WheelBaseItemsData, i ) + SAFE_DELETE( *i ); + m_WheelBaseItems.clear(); + m_WheelBaseItemsData.clear(); + m_LastSelection = NULL; +} + +void WheelBase::Load( CString sType ) +{ + LOG->Trace( "MusicWheel::Load('%s')", sType.c_str() ); + + SWITCH_SECONDS .Load(sType,"SwitchSeconds"); + ROULETTE_SWITCH_SECONDS .Load(sType,"RouletteSwitchSeconds"); + ROULETTE_SLOW_DOWN_SWITCHES .Load(sType,"RouletteSlowDownSwitches"); + LOCKED_INITIAL_VELOCITY .Load(sType,"LockedInitialVelocity"); + SCROLL_BAR_X .Load(sType,"ScrollBarX"); + SCROLL_BAR_HEIGHT .Load(sType,"ScrollBarHeight"); + ITEM_CURVE_X .Load(sType,"ItemCurveX"); + USE_LINEAR_WHEEL .Load(sType,"NoCurving"); + ITEM_SPACING_Y .Load(sType,"ItemSpacingY"); + WHEEL_3D_RADIUS .Load(sType,"Wheel3DRadius"); + CIRCLE_PERCENT .Load(sType,"CirclePercent"); + NUM_SECTION_COLORS .Load(sType,"NumSectionColors"); + SONG_REAL_EXTRA_COLOR .Load(sType,"SongRealExtraColor"); + SORT_MENU_COLOR .Load(sType,"SortMenuColor"); + SHOW_ROULETTE .Load(sType,"ShowRoulette"); + SHOW_RANDOM .Load(sType,"ShowRandom"); + SHOW_PORTAL .Load(sType,"ShowPortal"); + USE_3D .Load(sType,"Use3D"); + NUM_WHEEL_ITEMS_TO_DRAW .Load(sType,"NumWheelItems"); + MOST_PLAYED_SONGS_TO_SHOW .Load(sType,"MostPlayedSongsToShow"); + MODE_MENU_CHOICE_NAMES .Load(sType,"ModeMenuChoiceNames"); + vector vsModeChoiceNames; + split( MODE_MENU_CHOICE_NAMES, ",", vsModeChoiceNames ); + CHOICE .Load(sType,CHOICE_NAME,vsModeChoiceNames); + WHEEL_ITEM_ON_DELAY_CENTER .Load(sType,"WheelItemOnDelayCenter"); + WHEEL_ITEM_ON_DELAY_OFFSET .Load(sType,"WheelItemOnDelayOffset"); + WHEEL_ITEM_OFF_DELAY_CENTER .Load(sType,"WheelItemOffDelayCenter"); + WHEEL_ITEM_OFF_DELAY_OFFSET .Load(sType,"WheelItemOffDelayOffset"); +// SECTION_COLORS .Load(sType,SECTION_COLORS_NAME,NUM_SECTION_COLORS); + + FOREACH( WheelItemBase*, m_WheelBaseItems, i ) + SAFE_DELETE( *i ); + m_WheelBaseItems.clear(); + for( int i=0; iGetPathG(sType,"highlight") ); + m_sprHighlight->SetName( "Highlight" ); + this->AddChild( m_sprHighlight ); + ActorUtil::OnCommand( m_sprHighlight, sType ); + + m_ScrollBar.SetX( SCROLL_BAR_X ); + m_ScrollBar.SetBarHeight( SCROLL_BAR_HEIGHT ); + this->AddChild( &m_ScrollBar ); + + m_soundChangeMusic.Load( THEME->GetPathS(sType,"change"), true ); + m_soundLocked.Load( THEME->GetPathS(sType,"locked"), true ); + + m_iSelection = 0; + + m_WheelState = STATE_SELECTING_GENERIC; + m_fTimeLeftInState = 0; + m_fPositionOffsetFromSelection = 0; + + m_iSwitchesLeftInSpinDown = 0; + m_Moving = 0; + + BuildWheelItemsData(m_WheelBaseItemsData); + RebuildWheelItems(); +} + +void WheelBase::GetItemPosition( float fPosOffsetsFromMiddle, float& fX_out, float& fY_out, float& fZ_out, float& fRotationX_out ) +{ + if( USE_3D ) + { + const float curve = CIRCLE_PERCENT*2*PI; + fRotationX_out = SCALE(fPosOffsetsFromMiddle,-NUM_WHEEL_ITEMS/2.0f,+NUM_WHEEL_ITEMS/2.0f,-curve/2.f,+curve/2.f); + fX_out = (1-cosf(fPosOffsetsFromMiddle/PI))*ITEM_CURVE_X; + fY_out = WHEEL_3D_RADIUS*sinf(fRotationX_out); + fZ_out = -100+WHEEL_3D_RADIUS*cosf(fRotationX_out); + fRotationX_out *= 180.f/PI; // to degrees + +// printf( "fRotationX_out = %f\n", fRotationX_out ); + } + else if(!USE_LINEAR_WHEEL.GetValue()) + { + fX_out = (1-cosf(fPosOffsetsFromMiddle/PI))*ITEM_CURVE_X; + fY_out = fPosOffsetsFromMiddle*ITEM_SPACING_Y; + fZ_out = 0; + fRotationX_out = 0; + + fX_out = roundf( fX_out ); + fY_out = roundf( fY_out ); + fZ_out = roundf( fZ_out ); + } + else + { + fX_out = fPosOffsetsFromMiddle*ITEM_CURVE_X; + fY_out = fPosOffsetsFromMiddle*ITEM_SPACING_Y; + fZ_out = 0; + fRotationX_out = 0; + + fX_out = roundf( fX_out ); + fY_out = roundf( fY_out ); + fZ_out = roundf( fZ_out ); + } +} + +void WheelBase::SetItemPosition( Actor &item, float fPosOffsetsFromMiddle ) +{ + float fX, fY, fZ, fRotationX; + GetItemPosition( fPosOffsetsFromMiddle, fX, fY, fZ, fRotationX ); + item.SetXY( fX, fY ); + item.SetZ( fZ ); + item.SetRotationX( fRotationX ); +} + +void WheelBase::DrawPrimitives() +{ + if( USE_3D ) + { +// DISPLAY->PushMatrix(); +// DISPLAY->EnterPerspective(45, false); //Not needed for 3D + + // construct view and project matrix +// RageVector3 Up( 0.0f, 1.0f, 0.0f ); +// RageVector3 Eye( SCREEN_CENTER_X, SCREEN_CENTER_Y, 550 ); +// RageVector3 At( SCREEN_CENTER_X, SCREEN_CENTER_Y, 0 ); + +// DISPLAY->LoadLookAt(60, Eye, At, Up); //Changed from DISPLAY->LookAt(Eye, At, Up) + } + + // draw outside->inside + for( int i=0; i=NUM_WHEEL_ITEMS/2; i-- ) + DrawItem( i ); + + + ActorFrame::DrawPrimitives(); + + if( USE_3D ) + { +// DISPLAY->ExitPerspective(); //Not needed? +// DISPLAY->PopMatrix(); + } +} + + +void WheelBase::DrawItem( int i ) +{ + WheelItemBase *display = m_WheelBaseItems[i]; + + const float fThisBannerPositionOffsetFromSelection = i - NUM_WHEEL_ITEMS/2 + m_fPositionOffsetFromSelection; + if( fabsf(fThisBannerPositionOffsetFromSelection) > NUM_WHEEL_ITEMS_TO_DRAW/2 ) + return; + + switch( m_WheelState ) + { + case STATE_SELECTING_GENERIC: + case STATE_LOCKED: + { + SetItemPosition( *display, fThisBannerPositionOffsetFromSelection ); + } + break; + } + + if( m_WheelState == STATE_LOCKED && i != NUM_WHEEL_ITEMS/2 ) + display->m_fPercentGray = 0.5f; + else + display->m_fPercentGray = 0; + + display->Draw(); +} + + +void WheelBase::UpdateScrollbar() +{ + int total_num_items = m_WheelBaseItemsData.size(); + float item_at=m_iSelection - m_fPositionOffsetFromSelection; + + if(NUM_WHEEL_ITEMS >= total_num_items) { + m_ScrollBar.SetPercentage( 0, 1 ); + } else { + float size = float(NUM_WHEEL_ITEMS) / total_num_items; + float center = item_at / total_num_items; + size *= 0.5f; + + m_ScrollBar.SetPercentage( center - size, center + size ); + } +} + +bool WheelBase::IsSettled() const +{ + if( m_Moving ) + return false; + if( m_WheelState != STATE_SELECTING_GENERIC && m_WheelState != STATE_LOCKED ) + return false; + if( m_fPositionOffsetFromSelection != 0 ) + return false; + + return true; +} + + +void WheelBase::Update( float fDeltaTime ) +{ + ActorFrame::Update( fDeltaTime ); + + for( unsigned i = 0; i < unsigned(NUM_WHEEL_ITEMS); i++) + { + m_WheelBaseItems[i]->Update( fDeltaTime ); + } + + UpdateScrollbar(); + + if( m_Moving ) + { + m_TimeBeforeMovingBegins -= fDeltaTime; + m_TimeBeforeMovingBegins = max(m_TimeBeforeMovingBegins, 0); + } + + // update wheel state + m_fTimeLeftInState -= fDeltaTime; + if( m_fTimeLeftInState <= 0 ) // time to go to a new state + { + switch( m_WheelState ) + { + case STATE_TWEENING_ON_SCREEN: + m_fTimeLeftInState = 0; + if( (GAMESTATE->IsExtraStage() && !PREFSMAN->m_bPickExtraStage) || GAMESTATE->IsExtraStage2() ) + { + m_WheelState = STATE_LOCKED; + SCREENMAN->PlayStartSound(); + m_fLockedWheelVelocity = 0; + } + else + { + m_WheelState = STATE_SELECTING_GENERIC; + } + break; + case STATE_TWEENING_OFF_SCREEN: + m_WheelState = STATE_WAITING_OFF_SCREEN; + m_fTimeLeftInState = 0; + break; + case STATE_SELECTING_GENERIC: + m_fTimeLeftInState = 0; + break; + case STATE_WAITING_OFF_SCREEN: + break; + case STATE_LOCKED: + break; + default: + ASSERT(0); // all state changes should be handled explicitly + break; + } + } + + if( m_WheelState == STATE_LOCKED ) + { + /* Do this in at most .1 sec chunks, so we don't get weird if we + * stop for some reason (and so it behaves the same when being + * single stepped). */ + float tm = fDeltaTime; + while(tm > 0) + { + float t = min(tm, 0.1f); + tm -= t; + + m_fPositionOffsetFromSelection = clamp( m_fPositionOffsetFromSelection, -0.3f, +0.3f ); + + float fSpringForce = - m_fPositionOffsetFromSelection * LOCKED_INITIAL_VELOCITY; + m_fLockedWheelVelocity += fSpringForce; + + float fDrag = -m_fLockedWheelVelocity * t*4; + m_fLockedWheelVelocity += fDrag; + + m_fPositionOffsetFromSelection += m_fLockedWheelVelocity*t; + + if( fabsf(m_fPositionOffsetFromSelection) < 0.01f && fabsf(m_fLockedWheelVelocity) < 0.01f ) + { + m_fPositionOffsetFromSelection = 0; + m_fLockedWheelVelocity = 0; + } + } + } + + if( IsMoving() ) + { + /* We're automatically moving. Move linearly, and don't clamp + * to the selection. */ + float fSpinSpeed = m_SpinSpeed*m_Moving; + m_fPositionOffsetFromSelection -= fSpinSpeed*fDeltaTime; + + /* Make sure that we don't go further than 1 away, in case the + * speed is very high or we miss a lot of frames. */ + m_fPositionOffsetFromSelection = clamp(m_fPositionOffsetFromSelection, -1.0f, 1.0f); + + /* If it passed the selection, move again. */ + if((m_Moving == -1 && m_fPositionOffsetFromSelection >= 0) || + (m_Moving == 1 && m_fPositionOffsetFromSelection <= 0)) + { + ChangeMusic(m_Moving); + + if(PREFSMAN->m_iMusicWheelSwitchSpeed < MAX_WHEEL_SOUND_SPEED) + m_soundChangeMusic.Play(); + } + + if(PREFSMAN->m_iMusicWheelSwitchSpeed >= MAX_WHEEL_SOUND_SPEED && + m_MovingSoundTimer.PeekDeltaTime() >= 1.0f / MAX_WHEEL_SOUND_SPEED) + { + m_MovingSoundTimer.GetDeltaTime(); + m_soundChangeMusic.Play(); + } + } + else + { + // "rotate" wheel toward selected song + float fSpinSpeed = 0.2f + fabsf(m_fPositionOffsetFromSelection)/SWITCH_SECONDS; + + if( m_fPositionOffsetFromSelection > 0 ) + { + m_fPositionOffsetFromSelection -= fSpinSpeed*fDeltaTime; + if( m_fPositionOffsetFromSelection < 0 ) + m_fPositionOffsetFromSelection = 0; + } + else if( m_fPositionOffsetFromSelection < 0 ) + { + m_fPositionOffsetFromSelection += fSpinSpeed*fDeltaTime; + if( m_fPositionOffsetFromSelection > 0 ) + m_fPositionOffsetFromSelection = 0; + } + } +} + +bool WheelBase::Select() // return true if this selection ends the screen +{ + LOG->Trace( "WheelBase::Select()" ); + + m_Moving = 0; + + switch( m_WheelBaseItemsData[m_iSelection]->m_Type ) + { + case TYPE_GENERIC: + m_LastSelection = m_WheelBaseItemsData[m_iSelection]; + return false; + case TYPE_GROUP: + { + CString sThisItemSectionName = m_WheelBaseItemsData[m_iSelection]->m_sText; + if( m_sExpandedSectionName == sThisItemSectionName ) // already expanded + m_sExpandedSectionName = ""; // collapse it + else // already collapsed + m_sExpandedSectionName = sThisItemSectionName; // expand it + + m_soundExpand.Play(); + } + return false; + default: + ASSERT(0); + return false; + } +} + +int WheelBase::IsMoving() const +{ + return m_Moving && m_TimeBeforeMovingBegins == 0; +} + +void WheelBase::TweenOnScreen(bool changing_sort) +{ + m_WheelState = STATE_TWEENING_ON_SCREEN; + + SetItemPosition( *m_sprHighlight, 0 ); + + COMMAND( m_sprHighlight, "StartOn"); + if( changing_sort ) + { + const float delay = fabsf(NUM_WHEEL_ITEMS/2-WHEEL_ITEM_ON_DELAY_CENTER) * WHEEL_ITEM_ON_DELAY_OFFSET; + m_sprHighlight->BeginTweening( delay ); // sleep + COMMAND( m_sprHighlight, "FinishOnSort"); + } else { + COMMAND( m_sprHighlight, "FinishOn"); + } + + m_ScrollBar.SetX( SCROLL_BAR_X ); + m_ScrollBar.AddX( 30 ); + if(changing_sort) + m_ScrollBar.BeginTweening( 0.2f ); // sleep + else + m_ScrollBar.BeginTweening( 0.7f ); // sleep + m_ScrollBar.BeginTweening( 0.2f , Actor::TWEEN_ACCELERATE ); + m_ScrollBar.AddX( -30 ); + + for( int i=0; iBeginTweening( delay ); // sleep + COMMAND( display, "FinishOn"); + if( changing_sort ) + display->HurryTweening( 0.25f ); + } + + if( changing_sort ) + HurryTweening( 0.25f ); + + m_fTimeLeftInState = GetTweenTimeLeft() + 0.100f; +} + +void WheelBase::TweenOffScreen(bool changing_sort) +{ + m_WheelState = STATE_TWEENING_OFF_SCREEN; + + SetItemPosition( *m_sprHighlight, 0 ); + + COMMAND( m_sprHighlight, "StartOff"); + if(changing_sort) + { + /* When changing sort, tween the overlay with the item in the center; + * having it separate looks messy when we're moving fast. */ + const float delay = fabsf(NUM_WHEEL_ITEMS/2-WHEEL_ITEM_ON_DELAY_CENTER) * WHEEL_ITEM_ON_DELAY_OFFSET; + m_sprHighlight->BeginTweening( delay ); // sleep + COMMAND( m_sprHighlight, "FinishOffSort"); + } else { + COMMAND( m_sprHighlight, "FinishOff"); + } + COMMAND( m_sprHighlight, "FinishOff"); + + m_ScrollBar.BeginTweening( 0 ); + m_ScrollBar.BeginTweening( 0.2f, Actor::TWEEN_ACCELERATE ); + m_ScrollBar.SetX( SCROLL_BAR_X+30 ); + + for( int i=0; iBeginTweening( delay ); // sleep + COMMAND( display, "FinishOff"); + if( changing_sort ) + display->HurryTweening( 0.25f ); + } + + if( changing_sort ) + HurryTweening( 0.25f ); + + m_fTimeLeftInState = GetTweenTimeLeft() + 0.100f; +} + +void WheelBase::Move(int n) +{ + if(n == m_Moving) + return; + + if( m_WheelState == STATE_LOCKED ) + { + if(n) + { + int iSign = n/abs(n); + m_fLockedWheelVelocity = iSign*LOCKED_INITIAL_VELOCITY; + m_soundLocked.Play(); + } + return; + } + + /* If we're not selecting, discard this. We won't ignore it; we'll + * get called again every time the key is repeated. */ + /* Still process Move(0) so we sometimes continue moving immediate + * after the sort change finished and before the repeat event causes a + * Move(0). -Chris */ + switch( m_WheelState ) + { + case STATE_SELECTING_GENERIC: + break; + case STATE_FLYING_OFF_BEFORE_NEXT_SORT: + case STATE_FLYING_ON_AFTER_NEXT_SORT: + if( n!= 0 ) + return; + break; + default: + return; // don't continue + } + + if(m_Moving != 0 && n == 0 && m_TimeBeforeMovingBegins == 0) + { + /* We were moving, and now we're stopping. If we're really close to + * the selection, move to the next one, so we have a chance to spin down + * smoothly. */ + if(fabsf(m_fPositionOffsetFromSelection) < 0.25f ) + ChangeMusic(m_Moving); + + /* Make sure the user always gets an SM_SongChanged when + * Moving() is 0, so the final banner, etc. always gets set. */ + SCREENMAN->PostMessageToTopScreen( SM_SongChanged, 0 ); + } + + m_TimeBeforeMovingBegins = 1/4.0f; + m_SpinSpeed = float(PREFSMAN->m_iMusicWheelSwitchSpeed); + m_Moving = n; + + if(m_Moving) + ChangeMusic(m_Moving); +} + +void WheelBase::AddItem(WheelItemBaseData* itemdata) { + int visible, index; + m_WheelBaseItemsData.push_back(itemdata); + visible = FirstVisibleIndex(); + index = m_WheelBaseItemsData.size(); + + //If the item was shown in the wheel, rebuild the wheel + if ((0 <= (index - visible)) && ((index - visible) < NUM_WHEEL_ITEMS)) { + RebuildWheelItems(); + SCREENMAN->SystemMessageNoAnimate("redraw"); + } +} + +void WheelBase::ChangeMusic(int dist) +{ + m_iSelection += dist; + wrap( m_iSelection, m_WheelBaseItemsData.size() ); + + RebuildWheelItems( dist ); + + m_fPositionOffsetFromSelection += dist; + + SCREENMAN->PostMessageToTopScreen( SM_SongChanged, 0 ); + + /* If we're moving automatically, don't play this; it'll be called in Update. */ + if(!IsMoving()) + m_soundChangeMusic.Play(); +} + +void WheelBase::BuildWheelItemsData( vector &arrayWheelItemDatas ) { + if( arrayWheelItemDatas.empty() ) + { + arrayWheelItemDatas.push_back( new WheelItemBaseData(TYPE_GENERIC, "- EMPTY -", RageColor(1,0,0,1)) ); + } +} + +void WheelBase::RebuildWheelItems( int dist ) +{ + // rewind to first index that will be displayed; + int iFirstVisibleIndex = m_iSelection; + if( m_iSelection > int(m_WheelBaseItemsData.size()-1) ) + m_iSelection = 0; + + // find the first wheel item shown + iFirstVisibleIndex -= NUM_WHEEL_ITEMS/2; + + ASSERT(m_WheelBaseItemsData.size()); + wrap( iFirstVisibleIndex, m_WheelBaseItemsData.size() ); + + // iIndex is now the index of the lowest WheelItem to draw + + if( dist == -999999 ) + { + LOG->Info("data:%d", m_WheelBaseItemsData.size()); + // Refresh all + for( int i=0; iInfo("i:%d", i); + int iIndex = iFirstVisibleIndex + i; + wrap( iIndex, m_WheelBaseItemsData.size() ); + LOG->Info("iIndex:%d", iIndex); + + + WheelItemBaseData *data = m_WheelBaseItemsData[iIndex]; + WheelItemBase *display = m_WheelBaseItems[i]; + LOG->Info("DISP"); + display->LoadFromWheelItemBaseData( data ); + } + } + else + { + // Shift items and refresh only those that have changed. + CircularShift( m_WheelBaseItems, dist ); + if( dist > 0 ) + { + for( int i=NUM_WHEEL_ITEMS-dist; iLoadFromWheelItemBaseData( data ); + } + } + else if( dist < 0 ) + { + for( int i=0; i<-dist; i++ ) + { + int iIndex = iFirstVisibleIndex + i; + wrap( iIndex, m_WheelBaseItemsData.size() ); + WheelItemBaseData *data = m_WheelBaseItemsData[iIndex]; + WheelItemBase *display = m_WheelBaseItems[i]; + display->LoadFromWheelItemBaseData( data ); + } + } + } +} + +WheelItemBaseData* WheelBase::LastSelected( ) +{ + return m_LastSelection; +} + +void WheelBase::RemoveItem( int index ) { + if (index < m_WheelBaseItemsData.size()) { + vector::iterator i = m_WheelBaseItemsData.begin(); + i += index; + + //If this item's data happened to be last selected, make it NULL. + if (m_LastSelection == *i) + m_LastSelection = NULL; + + SAFE_DELETE( *i ); + m_WheelBaseItemsData.erase(i); + + RebuildWheelItems(); + } +} + +int WheelBase::FirstVisibleIndex() { + // rewind to first index that will be displayed; + int iFirstVisibleIndex = m_iSelection; + if( m_iSelection > int(m_WheelBaseItemsData.size()-1) ) + m_iSelection = 0; + + // find the first wheel item shown + iFirstVisibleIndex -= NUM_WHEEL_ITEMS/2; + + wrap( iFirstVisibleIndex, m_WheelBaseItemsData.size() ); + return iFirstVisibleIndex; +} + +/* + * (c) 2001-2004 Chris Danford, Chris Gomez, Glenn Maynard, Josh Allen + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/WheelBase.h b/stepmania/src/WheelBase.h new file mode 100644 index 0000000000..d5a737a3d4 --- /dev/null +++ b/stepmania/src/WheelBase.h @@ -0,0 +1,155 @@ +/* WheelBase - A wheel with data eliments. */ + +#ifndef WHEELBASE_H +#define WHEELBASE_H + +#include "AutoActor.h" +#include "ActorFrame.h" +#include "RageSound.h" +#include "RandomSample.h" +#include "GameConstantsAndTypes.h" +#include "MusicSortDisplay.h" +#include "ScreenMessage.h" +#include "ScoreDisplayNormal.h" +#include "ScrollBar.h" +#include "RageTimer.h" +#include "WheelItemBase.h" +#include "ThemeMetric.h" + +extern const AutoScreenMessage SM_SongChanged; // TODO: Replace this with a Message and MESSAGEMAN + +//struct CompareSongPointerArrayBySectionName; + +class WheelBase : public ActorFrame +{ +public: + WheelBase(); + virtual ~WheelBase(); + virtual void Load( CString sType ); + + virtual void Update( float fDeltaTime ); + virtual void DrawPrimitives(); + void DrawItem( int index ); + + virtual void TweenOnScreen(bool changing_sort); + virtual void TweenOffScreen(bool changing_sort); + virtual void TweenOnScreen() { TweenOnScreen(false); } + virtual void TweenOffScreen() { TweenOffScreen(false); } + + void Move(int n); + void ChangeMusic(int dist); /* +1 or -1 */ + + /* Return true if we're moving fast automatically. */ + int IsMoving() const; + bool IsSettled() const; + + void GetItemPosition( float fPosOffsetsFromMiddle, float& fX_out, float& fY_out, float& fZ_out, float& fRotationX_out ); + void SetItemPosition( Actor &item, float fPosOffsetsFromMiddle ); + + bool Select(); // return true if this selection ends the screen + + bool WheelIsLocked() { return (m_WheelState == STATE_LOCKED ? true : false); } + void RebuildWheelItems( int dist = -999999 ); // -999999 = refresh all + + void AddItem( WheelItemBaseData* itemdata ); + void RemoveItem( int index ); + WheelItemBaseData* LastSelected(); + +protected: + virtual void BuildWheelItemsData( vector &arrayWheelItemDatas ); +// bool SelectModeMenuItem(); + int FirstVisibleIndex(); + + ScrollBar m_ScrollBar; + AutoActor m_sprHighlight; + + vector m_WheelBaseItemsData; + vector m_WheelBaseItems; + WheelItemBaseData* m_LastSelection; + + int m_iSelection; // index into m_CurWheelItemBaseData + CString m_sExpandedSectionName; + + + int m_iSwitchesLeftInSpinDown; + float m_fLockedWheelVelocity; + /* 0 = none; -1 or 1 = up/down */ + int m_Moving; + RageTimer m_MovingSoundTimer; + float m_TimeBeforeMovingBegins; + float m_SpinSpeed; + enum WheelState { + STATE_SELECTING_GENERIC, + STATE_FLYING_OFF_BEFORE_NEXT_SORT, + STATE_FLYING_ON_AFTER_NEXT_SORT, + STATE_TWEENING_ON_SCREEN, + STATE_TWEENING_OFF_SCREEN, + STATE_WAITING_OFF_SCREEN, + STATE_LOCKED, + }; + WheelState m_WheelState; + float m_fTimeLeftInState; + float m_fPositionOffsetFromSelection; + + RageSound m_soundChangeMusic; + RageSound m_soundExpand; + RageSound m_soundLocked; + +// bool WheelItemIsVisible(int n); + void UpdateScrollbar(); + + ThemeMetric SWITCH_SECONDS; + ThemeMetric ROULETTE_SWITCH_SECONDS; + ThemeMetric ROULETTE_SLOW_DOWN_SWITCHES; + ThemeMetric LOCKED_INITIAL_VELOCITY; + ThemeMetric SCROLL_BAR_X; + ThemeMetric SCROLL_BAR_HEIGHT; + ThemeMetric ITEM_CURVE_X; + ThemeMetric USE_LINEAR_WHEEL; + ThemeMetric ITEM_SPACING_Y; + ThemeMetric WHEEL_3D_RADIUS; + ThemeMetric CIRCLE_PERCENT; + ThemeMetric NUM_SECTION_COLORS; + ThemeMetric SONG_REAL_EXTRA_COLOR; + ThemeMetric SORT_MENU_COLOR; + ThemeMetric SHOW_ROULETTE; + ThemeMetric SHOW_RANDOM; + ThemeMetric SHOW_PORTAL; + ThemeMetric USE_3D; + ThemeMetric NUM_WHEEL_ITEMS_TO_DRAW; + ThemeMetric MOST_PLAYED_SONGS_TO_SHOW; + ThemeMetric MODE_MENU_CHOICE_NAMES; + ThemeMetricMap CHOICE; + ThemeMetric WHEEL_ITEM_ON_DELAY_CENTER; + ThemeMetric WHEEL_ITEM_ON_DELAY_OFFSET; + ThemeMetric WHEEL_ITEM_OFF_DELAY_CENTER; + ThemeMetric WHEEL_ITEM_OFF_DELAY_OFFSET; +// ThemeMetric1D SECTION_COLORS; +}; + +#endif + +/* + * (c) 2001-2004 Chris Danford, Chris Gomez, Glenn Maynard, Josh Allen + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/WheelItemBase.cpp b/stepmania/src/WheelItemBase.cpp new file mode 100644 index 0000000000..5a1e5e8c15 --- /dev/null +++ b/stepmania/src/WheelItemBase.cpp @@ -0,0 +1,173 @@ +#include "global.h" +#include "WheelItemBase.h" +#include "RageUtil.h" +#include "SongManager.h" +#include "GameManager.h" +#include "PrefsManager.h" +#include "ScreenManager.h" // for sending SM_PlayMusicSample +#include "RageLog.h" +#include "GameConstantsAndTypes.h" +#include "GameState.h" +#include "ThemeManager.h" +#include "Steps.h" +#include "song.h" +#include "Course.h" +#include "ProfileManager.h" +#include "ActorUtil.h" + + +// WheelItem stuff +#define ICON_X THEME->GetMetricF("WheelItemBase","IconX") +#define SONG_NAME_X THEME->GetMetricF("WheelItemBase","SongNameX") +#define SECTION_NAME_X THEME->GetMetricF("WheelItemBase","SectionNameX") +#define SECTION_ZOOM THEME->GetMetricF("WheelItemBase","SectionZoom") +#define ROULETTE_X THEME->GetMetricF("WheelItemBase","RouletteX") +#define ROULETTE_ZOOM THEME->GetMetricF("WheelItemBase","RouletteZoom") +#define GRADE_X( p ) THEME->GetMetricF("WheelItemBase",ssprintf("GradeP%dX",p+1)) + + + + +WheelItemBaseData::WheelItemBaseData( WheelItemType wit, CString sText, RageColor color ) +{ + m_Type = wit; + m_sText = sText; + m_color = color; + m_Flags = WheelNotifyIcon::Flags(); +} + + +WheelItemBase::WheelItemBase() +{ +// data = new WheelItemBaseData; +// data->m_Type = TYPE_GENERIC; + SetName( "WheelItemBase" ); + + m_fPercentGray = 0; + + m_sprBar.Load( THEME->GetPathG("WheelItemBase","bar") ); + m_sprBar.SetXY( 0, 0 ); + m_All.AddChild( &m_sprBar ); + + m_text.LoadFromFont( THEME->GetPathF("WheelItemBase","text") ); + m_text.SetShadowLength( 0 ); + m_text.SetVertAlign( align_middle ); + m_text.SetXY( SECTION_NAME_X, 0 ); + m_text.SetZoom( SECTION_ZOOM ); + m_All.AddChild( &m_text ); +} + +void WheelItemBase::LoadFromWheelItemBaseData( WheelItemBaseData* pWID ) +{ + ASSERT( pWID != NULL ); + + data = pWID; + + m_text.SetText(pWID->m_sText); + m_Type = pWID->m_Type; + m_color = pWID->m_color; + + // init type specific stuff + switch( pWID->m_Type ) + { + case TYPE_GENERIC: + { + m_text.SetText( data->m_sText ); + m_text.SetDiffuse( data->m_color ); + } + break; + default: + ASSERT( 0 ); // invalid type + } +} + +void WheelItemBase::Update( float fDeltaTime ) +{ + Actor::Update( fDeltaTime ); + + switch( data->m_Type ) + { + case TYPE_GENERIC: + m_sprBar.Update( fDeltaTime ); + m_text.Update( fDeltaTime ); + break; + default: + break;//ASSERT(0); + } +} + +void WheelItemBase::DrawPrimitives() +{ + Sprite *bar = NULL; + + switch( data->m_Type ) + { + case TYPE_GENERIC: + bar = &m_sprBar; + break; + default: ASSERT(0); + } + + bar = &m_sprBar; + bar->Draw(); + m_text.Draw(); + switch( data->m_Type ) + { + case TYPE_GENERIC: + m_text.Draw(); + break; + default: + ASSERT(0); + } + + if( m_fPercentGray > 0 ) + { + bar->SetGlow( RageColor(0,0,0,m_fPercentGray) ); + bar->SetDiffuse( RageColor(0,0,0,0) ); + bar->Draw(); + bar->SetDiffuse( RageColor(0,0,0,1) ); + bar->SetGlow( RageColor(0,0,0,0) ); + } +} + + +void WheelItemBase::SetZTestMode( ZTestMode mode ) +{ + ActorFrame::SetZTestMode( mode ); + + // set all sub-Actors + m_All.SetZTestMode( mode ); +} + +void WheelItemBase::SetZWrite( bool b ) +{ + ActorFrame::SetZWrite( b ); + + // set all sub-Actors + m_All.SetZWrite( b ); +} + +/* + * (c) 2001-2004 Chris Danford, Chris Gomez, Glenn Maynard, Josh Allen + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/WheelItemBase.h b/stepmania/src/WheelItemBase.h new file mode 100644 index 0000000000..5a5b5f9fe5 --- /dev/null +++ b/stepmania/src/WheelItemBase.h @@ -0,0 +1,87 @@ +/* WheelItemBase - An item on the wheel. */ + +#ifndef WHEELITEMBASE_H +#define WHEELITEMBASE_H + +#include "ActorFrame.h" +#include "GradeDisplay.h" +#include "BitmapText.h" +#include "WheelNotifyIcon.h" +#include "TextBanner.h" +#include "GameConstantsAndTypes.h" +#include "GameCommand.h" + +struct WheelItemBaseData; + +enum WheelItemType +{ + TYPE_GENERIC, + TYPE_GROUP +}; + +class WheelItemBase : public ActorFrame +{ +public: + WheelItemBase(); + WheelItemBaseData* data; + virtual void Update( float fDeltaTime ); + virtual void DrawPrimitives(); + virtual void SetZTestMode( ZTestMode mode ); + virtual void SetZWrite( bool b ); + + virtual void LoadFromWheelItemBaseData( WheelItemBaseData* pWID ); + + float m_fPercentGray; + + // for TYPE_GENERIC + Sprite m_sprBar; + BitmapText m_text; + + WheelItemType m_Type; + RageColor m_color; + + // for TYPE_SORT + ActorFrame m_All; +}; + +struct WheelItemBaseData +{ + WheelItemBaseData() {} + WheelItemBaseData( WheelItemType wit, CString sText, RageColor color ); + + WheelItemType m_Type; + CString m_sText; + RageColor m_color; // either text color or section background color + WheelNotifyIcon::Flags m_Flags; + + // for TYPE_SORT +// CString m_sLabel; +// GameCommand m_Action; +}; + +#endif + +/* + * (c) 2001-2004 Chris Danford, Chris Gomez, Glenn Maynard, Josh Allen + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */