diff --git a/src/Actors/Actor.cpp b/src/Actors/Actor.cpp new file mode 100644 index 0000000000..917f4cc45f --- /dev/null +++ b/src/Actors/Actor.cpp @@ -0,0 +1,1746 @@ +#include "global.h" +#include "Actor.h" +#include "RageDisplay.h" +#include "RageUtil.h" +#include "RageMath.h" +#include "RageLog.h" +#include "arch/Dialog/Dialog.h" +#include "Foreach.h" +#include "XmlFile.h" +#include "LuaBinding.h" +#include "ThemeManager.h" +#include "LuaReference.h" +#include "MessageManager.h" +#include "LightsManager.h" // for NUM_CabinetLight +#include "ActorUtil.h" +#include "Preference.h" +#include + +static Preference g_bShowMasks("ShowMasks", false); + +/** + * @brief Set up a hidden Actor that won't be drawn. + * + * It's useful to be able to construct a basic Actor in XML, in + * order to simply delay a Transition, or receive and send broadcasts. + * Since these actors will never draw, set them hidden by default. */ +class HiddenActor: public Actor +{ +public: + HiddenActor() { SetVisible(false); } + virtual HiddenActor *Copy() const; +}; +REGISTER_ACTOR_CLASS_WITH_NAME( HiddenActor, Actor ); + +float Actor::g_fCurrentBGMTime = 0, Actor::g_fCurrentBGMBeat; +float Actor::g_fCurrentBGMTimeNoOffset = 0, Actor::g_fCurrentBGMBeatNoOffset = 0; +vector Actor::g_vfCurrentBGMBeatPlayer(NUM_PlayerNumber, 0); +vector Actor::g_vfCurrentBGMBeatPlayerNoOffset(NUM_PlayerNumber, 0); + + +Actor *Actor::Copy() const { return new Actor(*this); } + +static float g_fCabinetLights[NUM_CabinetLight]; + +static const char *HorizAlignNames[] = { + "Left", + "Center", + "Right" +}; +XToString( HorizAlign ); +LuaXType( HorizAlign ); + +static const char *VertAlignNames[] = { + "Top", + "Middle", + "Bottom" +}; +XToString( VertAlign ); +LuaXType( VertAlign ); + +void Actor::SetBGMTime( float fTime, float fBeat, float fTimeNoOffset, float fBeatNoOffset ) +{ + g_fCurrentBGMTime = fTime; + g_fCurrentBGMBeat = fBeat; + + /* This timer is generally only used for effects tied to the background music + * when GameSoundManager is aligning music beats. Alignment doesn't handle + * g_fVisualDelaySeconds. */ + g_fCurrentBGMTimeNoOffset = fTimeNoOffset; + g_fCurrentBGMBeatNoOffset = fBeatNoOffset; +} + +void Actor::SetPlayerBGMBeat( PlayerNumber pn, float fBeat, float fBeatNoOffset ) +{ + g_vfCurrentBGMBeatPlayer[pn] = fBeat; + g_vfCurrentBGMBeatPlayerNoOffset[pn] = fBeatNoOffset; +} + +void Actor::SetBGMLight( int iLightNumber, float fCabinetLights ) +{ + ASSERT( iLightNumber < NUM_CabinetLight ); + g_fCabinetLights[iLightNumber] = fCabinetLights; +} + +void Actor::InitState() +{ + StopTweening(); + + m_pTempState = NULL; + + m_baseRotation = RageVector3( 0, 0, 0 ); + m_baseScale = RageVector3( 1, 1, 1 ); + m_fBaseAlpha = 1; + + m_start.Init(); + m_current.Init(); + + m_fHorizAlign = 0.5f; + m_fVertAlign = 0.5f; +#if defined(SSC_FUTURES) + for( unsigned i = 0; i < m_Effects.size(); ++i ) + m_Effects[i] = no_effect; +#else + m_Effect = no_effect; +#endif + m_fSecsIntoEffect = 0; + m_fEffectDelta = 0; + m_fEffectRampUp = 0.5f; + m_fEffectHoldAtHalf = 0; + m_fEffectRampDown = 0.5f; + m_fEffectHoldAtZero = 0; + m_fEffectOffset = 0; + m_EffectClock = CLOCK_TIMER; + m_vEffectMagnitude = RageVector3(0,0,10); + m_effectColor1 = RageColor(1,1,1,1); + m_effectColor2 = RageColor(1,1,1,1); + + m_bVisible = true; + m_fShadowLengthX = 0; + m_fShadowLengthY = 0; + m_ShadowColor = RageColor(0,0,0,0.5f); + m_bIsAnimating = true; + m_fHibernateSecondsLeft = 0; + m_iDrawOrder = 0; + + m_bTextureWrapping = false; + m_bTextureFiltering = true; + + m_BlendMode = BLEND_NORMAL; + m_fZBias = 0; + m_bClearZBuffer = false; + m_ZTestMode = ZTEST_OFF; + m_bZWrite = false; + m_CullMode = CULL_NONE; +} + +static bool GetMessageNameFromCommandName( const RString &sCommandName, RString &sMessageNameOut ) +{ + if( sCommandName.Right(7) == "Message" ) + { + sMessageNameOut = sCommandName.Left(sCommandName.size()-7); + return true; + } + else + { + return false; + } +} + +Actor::Actor() +{ + m_pLuaInstance = new LuaClass; + Lua *L = LUA->Get(); + m_pLuaInstance->PushSelf( L ); + lua_newtable( L ); + lua_pushvalue( L, -1 ); + lua_setmetatable( L, -2 ); + lua_setfield( L, -2, "ctx" ); + lua_pop( L, 1 ); + LUA->Release( L ); + + m_size = RageVector2( 1, 1 ); + InitState(); + m_pParent = NULL; + m_bFirstUpdate = true; +} + +Actor::~Actor() +{ + StopTweening(); + UnsubscribeAll(); +} + +Actor::Actor( const Actor &cpy ): + MessageSubscriber( cpy ) +{ + /* Don't copy an Actor in the middle of rendering. */ + ASSERT( cpy.m_pTempState == NULL ); + m_pTempState = NULL; + +#define CPY(x) x = cpy.x + CPY( m_sName ); + CPY( m_pParent ); + CPY( m_pLuaInstance ); + + CPY( m_baseRotation ); + CPY( m_baseScale ); + CPY( m_fBaseAlpha ); + + + CPY( m_size ); + CPY( m_current ); + CPY( m_start ); + for( unsigned i = 0; i < cpy.m_Tweens.size(); ++i ) + m_Tweens.push_back( new TweenStateAndInfo(*cpy.m_Tweens[i]) ); + + CPY( m_bFirstUpdate ); + + CPY( m_fHorizAlign ); + CPY( m_fVertAlign ); +#if defined(SSC_FUTURES) + // I'm a bit worried about this -aj + for( unsigned i = 0; i < cpy.m_Effects.size(); ++i ) + m_Effects.push_back( (*cpy.m_Effects[i]) ); +#else + CPY( m_Effect ); +#endif + CPY( m_fSecsIntoEffect ); + CPY( m_fEffectDelta ); + CPY( m_fEffectRampUp ); + CPY( m_fEffectHoldAtHalf ); + CPY( m_fEffectRampDown ); + CPY( m_fEffectHoldAtZero ); + CPY( m_fEffectOffset ); + CPY( m_EffectClock ); + + CPY( m_effectColor1 ); + CPY( m_effectColor2 ); + CPY( m_vEffectMagnitude ); + + CPY( m_bVisible ); + CPY( m_fHibernateSecondsLeft ); + CPY( m_fShadowLengthX ); + CPY( m_fShadowLengthY ); + CPY( m_ShadowColor ); + CPY( m_bIsAnimating ); + CPY( m_iDrawOrder ); + + CPY( m_bTextureWrapping ); + CPY( m_bTextureFiltering ); + CPY( m_BlendMode ); + CPY( m_bClearZBuffer ); + CPY( m_ZTestMode ); + CPY( m_bZWrite ); + CPY( m_fZBias ); + CPY( m_CullMode ); + + CPY( m_mapNameToCommands ); +#undef CPY +} + +/* XXX: This calls InitCommand, which must happen after all other + * initialization (eg. ActorFrame loading children). However, it + * also loads input variables, which should happen first. The + * former is more important. */ +void Actor::LoadFromNode( const XNode* pNode ) +{ + Lua *L = LUA->Get(); + FOREACH_CONST_Attr( pNode, pAttr ) + { + // Load Name, if any. + const RString &sKeyName = pAttr->first; + const XNodeValue *pValue = pAttr->second; + if( sKeyName == "Name" ) SetName( pValue->GetValue() ); + else if( sKeyName == "BaseRotationX" ) SetBaseRotationX( pValue->GetValue() ); + else if( sKeyName == "BaseRotationY" ) SetBaseRotationY( pValue->GetValue() ); + else if( sKeyName == "BaseRotationZ" ) SetBaseRotationZ( pValue->GetValue() ); + else if( sKeyName == "BaseZoomX" ) SetBaseZoomX( pValue->GetValue() ); + else if( sKeyName == "BaseZoomY" ) SetBaseZoomY( pValue->GetValue() ); + else if( sKeyName == "BaseZoomZ" ) SetBaseZoomZ( pValue->GetValue() ); + else if( EndsWith(sKeyName,"Command") ) + { + LuaReference *pRef = new LuaReference; + pValue->PushValue( L ); + pRef->SetFromStack( L ); + RString sCmdName = sKeyName.Left( sKeyName.size()-7 ); + AddCommand( sCmdName, apActorCommands( pRef ) ); + } + } + + LUA->Release( L ); + + // Don't recurse Init. It gets called once for every Actor when the + // Actor is loaded, and we don't want to call it again. + PlayCommandNoRecurse( Message("Init") ); +} + +void Actor::Draw() +{ + if( !m_bVisible ) + return; // early abort + if( m_fHibernateSecondsLeft > 0 ) + return; // early abort + if( this->EarlyAbortDraw() ) + return; + + // call the most-derived versions + this->BeginDraw(); + this->DrawPrimitives(); // call the most-derived version of DrawPrimitives(); + this->EndDraw(); +} + +void Actor::BeginDraw() // set the world matrix and calculate actor properties +{ + DISPLAY->PushMatrix(); // we're actually going to do some drawing in this function + + // Somthing below may set m_pTempState to tempState + m_pTempState = &m_current; + + // set temporary drawing properties based on Effects + static TweenState tempState; + + // todo: account for SSC_FUTURES -aj + if( m_Effect == no_effect ) + { + } + else + { + m_pTempState = &tempState; + tempState = m_current; + + const float fTotalPeriod = GetEffectPeriod(); + ASSERT( fTotalPeriod > 0 ); + const float fTimeIntoEffect = fmodfp( m_fSecsIntoEffect+m_fEffectOffset, fTotalPeriod ); + + float fPercentThroughEffect; + if( fTimeIntoEffect < m_fEffectRampUp ) + { + fPercentThroughEffect = SCALE( + fTimeIntoEffect, + 0, + m_fEffectRampUp, + 0.0f, + 0.5f ); + } + else if( fTimeIntoEffect < m_fEffectRampUp + m_fEffectHoldAtHalf ) + { + fPercentThroughEffect = 0.5f; + } + else if( fTimeIntoEffect < m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown ) + { + fPercentThroughEffect = SCALE( + fTimeIntoEffect, + m_fEffectRampUp + m_fEffectHoldAtHalf, + m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown, + 0.5f, + 1.0f ); + } + else + { + fPercentThroughEffect = 0; + } + ASSERT_M( fPercentThroughEffect >= 0 && fPercentThroughEffect <= 1, + ssprintf("PercentThroughEffect: %f", fPercentThroughEffect) ); + + bool bBlinkOn = fPercentThroughEffect > 0.5f; + float fPercentBetweenColors = RageFastSin( (fPercentThroughEffect + 0.25f) * 2 * PI ) / 2 + 0.5f; + ASSERT_M( fPercentBetweenColors >= 0 && fPercentBetweenColors <= 1, + ssprintf("PercentBetweenColors: %f, PercentThroughEffect: %f", fPercentBetweenColors, fPercentThroughEffect) ); + float fOriginalAlpha = tempState.diffuse[0].a; + + // todo: account for SSC_FUTURES -aj + switch( m_Effect ) + { + case diffuse_blink: + /* XXX: Should diffuse_blink and diffuse_shift multiply the tempState color? + * (That would have the same effect with 1,1,1,1, and allow tweening the diffuse + * while blinking and shifting.) */ + for(int i=0; i<4; i++) + { + tempState.diffuse[i] = bBlinkOn ? m_effectColor1 : m_effectColor2; + tempState.diffuse[i].a *= fOriginalAlpha; // multiply the alphas so we can fade even while an effect is playing + } + break; + case diffuse_shift: + for(int i=0; i<4; i++) + { + tempState.diffuse[i] = m_effectColor1*fPercentBetweenColors + m_effectColor2*(1.0f-fPercentBetweenColors); + tempState.diffuse[i].a *= fOriginalAlpha; // multiply the alphas so we can fade even while an effect is playing + } + break; + case diffuse_ramp: + for(int i=0; i<4; i++) + { + tempState.diffuse[i] = m_effectColor1*fPercentThroughEffect + m_effectColor2*(1.0f-fPercentThroughEffect); + tempState.diffuse[i].a *= fOriginalAlpha; // multiply the alphas so we can fade even while an effect is playing + } + break; + case glow_blink: + tempState.glow = bBlinkOn ? m_effectColor1 : m_effectColor2; + tempState.glow.a *= fOriginalAlpha; // don't glow if the Actor is transparent! + break; + case glow_shift: + tempState.glow = m_effectColor1*fPercentBetweenColors + m_effectColor2*(1.0f-fPercentBetweenColors); + tempState.glow.a *= fOriginalAlpha; // don't glow if the Actor is transparent! + break; + case glow_ramp: + tempState.glow = m_effectColor1*fPercentThroughEffect + m_effectColor2*(1.0f-fPercentThroughEffect); + tempState.glow.a *= fOriginalAlpha; // don't glow if the Actor is transparent! + break; + case rainbow: + tempState.diffuse[0] = RageColor( + RageFastCos( fPercentBetweenColors*2*PI ) * 0.5f + 0.5f, + RageFastCos( fPercentBetweenColors*2*PI + PI * 2.0f / 3.0f ) * 0.5f + 0.5f, + RageFastCos( fPercentBetweenColors*2*PI + PI * 4.0f / 3.0f) * 0.5f + 0.5f, + fOriginalAlpha ); + for( int i=1; i<4; i++ ) + tempState.diffuse[i] = tempState.diffuse[0]; + break; + case wag: + tempState.rotation += m_vEffectMagnitude * RageFastSin( fPercentThroughEffect * 2.0f * PI ); + break; + case spin: + // nothing needs to be here + break; + case vibrate: + tempState.pos.x += m_vEffectMagnitude.x * randomf(-1.0f, 1.0f) * GetZoom(); + tempState.pos.y += m_vEffectMagnitude.y * randomf(-1.0f, 1.0f) * GetZoom(); + tempState.pos.z += m_vEffectMagnitude.z * randomf(-1.0f, 1.0f) * GetZoom(); + break; + case bounce: + { + float fPercentOffset = RageFastSin( fPercentThroughEffect*PI ); + tempState.pos += m_vEffectMagnitude * fPercentOffset; + tempState.pos.x = roundf( tempState.pos.x ); + tempState.pos.y = roundf( tempState.pos.y ); + tempState.pos.z = roundf( tempState.pos.z ); + } + break; + case bob: + { + float fPercentOffset = RageFastSin( fPercentThroughEffect*PI*2 ); + tempState.pos += m_vEffectMagnitude * fPercentOffset; + tempState.pos.x = roundf( tempState.pos.x ); + tempState.pos.y = roundf( tempState.pos.y ); + tempState.pos.z = roundf( tempState.pos.z ); + } + break; + case pulse: + { + float fMinZoom = m_vEffectMagnitude[0]; + float fMaxZoom = m_vEffectMagnitude[1]; + float fPercentOffset = RageFastSin( fPercentThroughEffect*PI ); + float fZoom = SCALE( fPercentOffset, 0.f, 1.f, fMinZoom, fMaxZoom ); + tempState.scale *= fZoom; + + // Use the color as a Vector3 to scale the effect for added control + RageColor c = SCALE( fPercentOffset, 0.f, 1.f, m_effectColor1, m_effectColor2 ); + tempState.scale.x *= c.r; + tempState.scale.y *= c.g; + tempState.scale.z *= c.b; + } + break; + default: + ASSERT(0); // invalid Effect + } + } + + if( m_fBaseAlpha != 1 ) + { + if( m_pTempState != &tempState ) + { + m_pTempState = &tempState; + tempState = m_current; + } + + for( int i=0; i<4; i++ ) + tempState.diffuse[i].a *= m_fBaseAlpha; + } + + if( m_pTempState->pos.x != 0 || m_pTempState->pos.y != 0 || m_pTempState->pos.z != 0 ) + { + RageMatrix m; + RageMatrixTranslate( + &m, + m_pTempState->pos.x, + m_pTempState->pos.y, + m_pTempState->pos.z + ); + DISPLAY->PreMultMatrix( m ); + } + + { + /* The only time rotation and quat should normally be used simultaneously + * is for m_baseRotation. Most objects aren't rotated at all, so optimize + * that case. */ + const float fRotateX = m_pTempState->rotation.x + m_baseRotation.x; + const float fRotateY = m_pTempState->rotation.y + m_baseRotation.y; + const float fRotateZ = m_pTempState->rotation.z + m_baseRotation.z; + + if( fRotateX != 0 || fRotateY != 0 || fRotateZ != 0 ) + { + RageMatrix m; + RageMatrixRotationXYZ( &m, fRotateX, fRotateY, fRotateZ ); + DISPLAY->PreMultMatrix( m ); + } + } + + // handle scaling + { + const float fScaleX = m_pTempState->scale.x * m_baseScale.x; + const float fScaleY = m_pTempState->scale.y * m_baseScale.y; + const float fScaleZ = m_pTempState->scale.z * m_baseScale.z; + + if( fScaleX != 1 || fScaleY != 1 || fScaleZ != 1 ) + { + RageMatrix m; + RageMatrixScale( + &m, + fScaleX, + fScaleY, + fScaleZ ); + DISPLAY->PreMultMatrix( m ); + } + } + + // handle alignment; most actors have default alignment. + if( unlikely(m_fHorizAlign != 0.5f || m_fVertAlign != 0.5f) ) + { + float fX = SCALE( m_fHorizAlign, 0.0f, 1.0f, +m_size.x/2.0f, -m_size.x/2.0f ); + float fY = SCALE( m_fVertAlign, 0.0f, 1.0f, +m_size.y/2.0f, -m_size.y/2.0f ); + RageMatrix m; + RageMatrixTranslate( + &m, + fX, + fY, + 0 + ); + DISPLAY->PreMultMatrix( m ); + } + + if( m_pTempState->quat.x != 0 || m_pTempState->quat.y != 0 || m_pTempState->quat.z != 0 || m_pTempState->quat.w != 1 ) + { + RageMatrix mat; + RageMatrixFromQuat( &mat, m_pTempState->quat ); + + DISPLAY->MultMatrix(mat); + } + + // handle skews + if( m_pTempState->fSkewX != 0 ) + { + DISPLAY->SkewX( m_pTempState->fSkewX ); + } + + if( m_pTempState->fSkewY != 0 ) + { + DISPLAY->SkewY( m_pTempState->fSkewY ); + } + +} + +void Actor::SetGlobalRenderStates() +{ + // set Actor-defined render states + if( !g_bShowMasks.Get() || m_BlendMode != BLEND_NO_EFFECT ) + DISPLAY->SetBlendMode( m_BlendMode ); + DISPLAY->SetZWrite( m_bZWrite ); + DISPLAY->SetZTestMode( m_ZTestMode ); + + // BLEND_NO_EFFECT is used to draw masks to the Z-buffer, which always wants + // Z-bias enabled. + if( m_fZBias == 0 && m_BlendMode == BLEND_NO_EFFECT ) + DISPLAY->SetZBias( 1.0f ); + else + DISPLAY->SetZBias( m_fZBias ); + + if( m_bClearZBuffer ) + DISPLAY->ClearZBuffer(); + DISPLAY->SetCullMode( m_CullMode ); +} + +void Actor::SetTextureRenderStates() +{ + DISPLAY->SetTextureWrapping( TextureUnit_1, m_bTextureWrapping ); + DISPLAY->SetTextureFiltering( TextureUnit_1, m_bTextureFiltering ); +} + +void Actor::EndDraw() +{ + DISPLAY->PopMatrix(); + m_pTempState = NULL; +} + +void Actor::UpdateTweening( float fDeltaTime ) +{ + while( 1 ) + { + if( m_Tweens.empty() ) // nothing to do + return; + + if( fDeltaTime == 0 ) // nothing will change + return; + + // update current tween state + // earliest tween + TweenState &TS = m_Tweens[0]->state; + TweenInfo &TI = m_Tweens[0]->info; + + bool bBeginning = TI.m_fTimeLeftInTween == TI.m_fTweenTime; + + float fSecsToSubtract = min( TI.m_fTimeLeftInTween, fDeltaTime ); + TI.m_fTimeLeftInTween -= fSecsToSubtract; + fDeltaTime -= fSecsToSubtract; + + RString sCommand = TI.m_sCommandName; + if( bBeginning ) // we are just beginning this tween + m_start = m_current; // set the start position + + if( TI.m_fTimeLeftInTween == 0 ) // Current tween is over. Stop. + { + m_current = TS; + + // delete the head tween + delete m_Tweens.front(); + m_Tweens.erase( m_Tweens.begin() ); + } + else // in the middle of tweening. Recalcute the current position. + { + const float fPercentThroughTween = 1-(TI.m_fTimeLeftInTween / TI.m_fTweenTime); + + // distort the percentage if appropriate + float fPercentAlongPath = TI.m_pTween->Tween( fPercentThroughTween ); + TweenState::MakeWeightedAverage( m_current, m_start, TS, fPercentAlongPath ); + } + + if( bBeginning ) + { + // Execute the command in this tween (if any). Do this last, and don't + // access TI or TS after, since this may modify the tweening queue. + if( !sCommand.empty() ) + { + if( sCommand.Left(1) == "!" ) + MESSAGEMAN->Broadcast( sCommand.substr(1) ); + else + this->PlayCommand( sCommand ); + } + } + } +} + +bool Actor::IsFirstUpdate() const +{ + return m_bFirstUpdate; +} + +void Actor::Update( float fDeltaTime ) +{ +// LOG->Trace( "Actor::Update( %f )", fDeltaTime ); + ASSERT_M( fDeltaTime >= 0, ssprintf("DeltaTime: %f",fDeltaTime) ); + + if( m_fHibernateSecondsLeft > 0 ) + { + m_fHibernateSecondsLeft -= fDeltaTime; + if( m_fHibernateSecondsLeft > 0 ) + return; + + // Grab the leftover time. + fDeltaTime = -m_fHibernateSecondsLeft; + m_fHibernateSecondsLeft = 0; + } + + this->UpdateInternal( fDeltaTime ); +} + +void Actor::UpdateInternal( float fDeltaTime ) +{ + if( m_bFirstUpdate ) + m_bFirstUpdate = false; + + switch( m_EffectClock ) + { + case CLOCK_TIMER: + m_fSecsIntoEffect += fDeltaTime; + m_fEffectDelta = fDeltaTime; + + /* Wrap the counter, so it doesn't increase indefinitely (causing loss + * of precision if a screen is left to sit for a day). */ + if( m_fSecsIntoEffect >= GetEffectPeriod() ) + m_fSecsIntoEffect -= GetEffectPeriod(); + break; + + case CLOCK_TIMER_GLOBAL: + { + float fTime = RageTimer::GetTimeSinceStartFast(); + m_fEffectDelta = fTime - m_fSecsIntoEffect; + m_fSecsIntoEffect = fTime; + break; + } + + case CLOCK_BGM_BEAT: + m_fEffectDelta = g_fCurrentBGMBeat - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_fCurrentBGMBeat; + break; + + case CLOCK_BGM_BEAT_PLAYER1: + m_fEffectDelta = g_vfCurrentBGMBeatPlayer[PLAYER_1] - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_vfCurrentBGMBeatPlayerNoOffset[PLAYER_1]; + break; + + case CLOCK_BGM_BEAT_PLAYER2: + m_fEffectDelta = g_vfCurrentBGMBeatPlayer[PLAYER_2] - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_vfCurrentBGMBeatPlayerNoOffset[PLAYER_2]; + break; + + case CLOCK_BGM_TIME: + m_fEffectDelta = g_fCurrentBGMTime - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_fCurrentBGMTime; + break; + + case CLOCK_BGM_BEAT_NO_OFFSET: + m_fEffectDelta = g_fCurrentBGMBeatNoOffset - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_fCurrentBGMBeatNoOffset; + break; + + case CLOCK_BGM_TIME_NO_OFFSET: + m_fEffectDelta = g_fCurrentBGMTimeNoOffset - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_fCurrentBGMTimeNoOffset; + break; + + default: + if( m_EffectClock >= CLOCK_LIGHT_1 && m_EffectClock <= CLOCK_LIGHT_LAST ) + { + int i = m_EffectClock - CLOCK_LIGHT_1; + m_fEffectDelta = g_fCabinetLights[i] - m_fSecsIntoEffect; + m_fSecsIntoEffect = g_fCabinetLights[i]; + } + break; + } + + // update effect + // todo: account for SSC_FUTURES -aj + switch( m_Effect ) + { + case spin: + m_current.rotation += m_fEffectDelta*m_vEffectMagnitude; + wrap( m_current.rotation.x, 360 ); + wrap( m_current.rotation.y, 360 ); + wrap( m_current.rotation.z, 360 ); + break; + default: break; + } + + UpdateTweening( fDeltaTime ); +} + +RString Actor::GetLineage() const +{ + RString sPath; + + if( m_pParent ) + sPath = m_pParent->GetLineage() + '/'; + sPath += ssprintf( "<%s> %s", typeid(*this).name(), m_sName.c_str() ); + return sPath; +} + +void Actor::BeginTweening( float time, ITween *pTween ) +{ + ASSERT( time >= 0 ); + + time = max( time, 0 ); + + // If the number of tweens to ever gets this large, there's probably an infinitely + // recursing ActorCommand. + if( m_Tweens.size() > 50 ) + { + RString sError = ssprintf( "Tween overflow: \"%s\"; infinitely recursing ActorCommand?", GetLineage().c_str() ); + + LOG->Warn( "%s", sError.c_str() ); + Dialog::OK( sError ); + FinishTweening(); + } + + // add a new TweenState to the tail, and initialize it + m_Tweens.push_back( new TweenStateAndInfo ); + + // latest + TweenState &TS = m_Tweens.back()->state; + TweenInfo &TI = m_Tweens.back()->info; + + if( m_Tweens.size() >= 2 ) // if there was already a TS on the stack + { + // initialize the new TS from the last TS in the list + TS = m_Tweens[m_Tweens.size()-2]->state; + } + else + { + // This new TS is the only TS. + // Set our tween starting and ending values to the current position. + TS = m_current; + } + + TI.m_pTween = pTween; + TI.m_fTweenTime = time; + TI.m_fTimeLeftInTween = time; +} + +void Actor::BeginTweening( float time, TweenType tt ) +{ + ITween *pTween = ITween::CreateFromType( tt ); + BeginTweening( time, pTween ); +} + +void Actor::StopTweening() +{ + for( unsigned i = 0; i < m_Tweens.size(); ++i ) + delete m_Tweens[i]; + m_Tweens.clear(); +} + +void Actor::FinishTweening() +{ + if( !m_Tweens.empty() ) + m_current = DestTweenState(); + StopTweening(); +} + +void Actor::HurryTweening( float factor ) +{ + for( unsigned i = 0; i < m_Tweens.size(); ++i ) + { + m_Tweens[i]->info.m_fTimeLeftInTween *= factor; + m_Tweens[i]->info.m_fTweenTime *= factor; + } +} + +void Actor::ScaleTo( const RectF &rect, StretchType st ) +{ + // width and height of rectangle + float rect_width = rect.GetWidth(); + float rect_height = rect.GetHeight(); + + if( rect_width < 0 ) SetRotationY( 180 ); + if( rect_height < 0 ) SetRotationX( 180 ); + + // zoom fActor needed to scale the Actor to fill the rectangle + float fNewZoomX = fabsf(rect_width / m_size.x); + float fNewZoomY = fabsf(rect_height / m_size.y); + + float fNewZoom = 0.f; + switch( st ) + { + case cover: + fNewZoom = fNewZoomX>fNewZoomY ? fNewZoomX : fNewZoomY; // use larger zoom + break; + case fit_inside: + fNewZoom = fNewZoomX>fNewZoomY ? fNewZoomY : fNewZoomX; // use smaller zoom + break; + } + + SetX( rect.left + rect_width * m_fHorizAlign ); + SetY( rect.top + rect_height * m_fVertAlign ); + + SetZoom( fNewZoom ); +} + +void Actor::SetEffectClockString( const RString &s ) +{ + if (s.EqualsNoCase("timer")) this->SetEffectClock( CLOCK_TIMER ); + if (s.EqualsNoCase("timerglobal")) this->SetEffectClock( CLOCK_TIMER_GLOBAL ); + else if(s.EqualsNoCase("beat")) this->SetEffectClock( CLOCK_BGM_BEAT ); + else if(s.EqualsNoCase("music")) this->SetEffectClock( CLOCK_BGM_TIME ); + else if(s.EqualsNoCase("bgm")) this->SetEffectClock( CLOCK_BGM_BEAT ); // compat, deprecated + else if(s.EqualsNoCase("musicnooffset"))this->SetEffectClock( CLOCK_BGM_TIME_NO_OFFSET ); + else if(s.EqualsNoCase("beatnooffset")) this->SetEffectClock( CLOCK_BGM_BEAT_NO_OFFSET ); + else + { + CabinetLight cl = StringToCabinetLight( s ); + if( cl != CabinetLight_Invalid ) + { + this->SetEffectClock( (EffectClock) (cl + CLOCK_LIGHT_1) ); + return; + } + else + ASSERT(0); + } +} + +void Actor::StretchTo( const RectF &r ) +{ + // width and height of rectangle + float width = r.GetWidth(); + float height = r.GetHeight(); + + // center of the rectangle + float cx = r.left + width/2.0f; + float cy = r.top + height/2.0f; + + // zoom fActor needed to scale the Actor to fill the rectangle + float fNewZoomX = width / m_size.x; + float fNewZoomY = height / m_size.y; + + SetXY( cx, cy ); + SetZoomX( fNewZoomX ); + SetZoomY( fNewZoomY ); +} + + +void Actor::SetEffectPeriod( float fTime ) +{ + ASSERT( fTime > 0 ); + m_fEffectRampUp = fTime/2; + m_fEffectHoldAtHalf = 0; + m_fEffectRampDown = fTime/2; + m_fEffectHoldAtZero = 0; +} + +float Actor::GetEffectPeriod() const +{ + return m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown + m_fEffectHoldAtZero; +} + +void Actor::SetEffectTiming( float fRampUp, float fAtHalf, float fRampDown, float fAtZero ) +{ + m_fEffectRampUp = fRampUp; + m_fEffectHoldAtHalf = fAtHalf; + m_fEffectRampDown = fRampDown; + m_fEffectHoldAtZero = fAtZero; + ASSERT( GetEffectPeriod() > 0 ); +} + +// effect "macros" + +void Actor::SetEffectDiffuseBlink( float fEffectPeriodSeconds, RageColor c1, RageColor c2 ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != diffuse_blink ) + { + m_Effect = diffuse_blink; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); + m_effectColor1 = c1; + m_effectColor2 = c2; +} + +void Actor::SetEffectDiffuseShift( float fEffectPeriodSeconds, RageColor c1, RageColor c2 ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != diffuse_shift ) + { + m_Effect = diffuse_shift; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); + m_effectColor1 = c1; + m_effectColor2 = c2; +} + +void Actor::SetEffectDiffuseRamp( float fEffectPeriodSeconds, RageColor c1, RageColor c2 ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != diffuse_ramp ) + { + m_Effect = diffuse_ramp; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); + m_effectColor1 = c1; + m_effectColor2 = c2; +} + +void Actor::SetEffectGlowBlink( float fEffectPeriodSeconds, RageColor c1, RageColor c2 ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != glow_blink ) + { + m_Effect = glow_blink; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); + m_effectColor1 = c1; + m_effectColor2 = c2; +} + +void Actor::SetEffectGlowShift( float fEffectPeriodSeconds, RageColor c1, RageColor c2 ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != glow_shift ) + { + m_Effect = glow_shift; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); + m_effectColor1 = c1; + m_effectColor2 = c2; +} + +void Actor::SetEffectGlowRamp( float fEffectPeriodSeconds, RageColor c1, RageColor c2 ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != glow_ramp ) + { + m_Effect = glow_ramp; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); + m_effectColor1 = c1; + m_effectColor2 = c2; +} + +void Actor::SetEffectRainbow( float fEffectPeriodSeconds ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != rainbow ) + { + m_Effect = rainbow; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fEffectPeriodSeconds ); +} + +void Actor::SetEffectWag( float fPeriod, RageVector3 vect ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect != wag ) + { + m_Effect = wag; + m_fSecsIntoEffect = 0; + } + SetEffectPeriod( fPeriod ); + m_vEffectMagnitude = vect; +} + +void Actor::SetEffectBounce( float fPeriod, RageVector3 vect ) +{ + // todo: account for SSC_FUTURES -aj + m_Effect = bounce; + SetEffectPeriod( fPeriod ); + m_vEffectMagnitude = vect; + m_fSecsIntoEffect = 0; +} + +void Actor::SetEffectBob( float fPeriod, RageVector3 vect ) +{ + // todo: account for SSC_FUTURES -aj + if( m_Effect!=bob || GetEffectPeriod() != fPeriod ) + { + m_Effect = bob; + SetEffectPeriod( fPeriod ); + m_fSecsIntoEffect = 0; + } + m_vEffectMagnitude = vect; +} + +void Actor::SetEffectSpin( RageVector3 vect ) +{ + // todo: account for SSC_FUTURES -aj + m_Effect = spin; + m_vEffectMagnitude = vect; +} + +void Actor::SetEffectVibrate( RageVector3 vect ) +{ + // todo: account for SSC_FUTURES -aj + m_Effect = vibrate; + m_vEffectMagnitude = vect; +} + +void Actor::SetEffectPulse( float fPeriod, float fMinZoom, float fMaxZoom ) +{ + // todo: account for SSC_FUTURES -aj + m_Effect = pulse; + SetEffectPeriod( fPeriod ); + m_vEffectMagnitude[0] = fMinZoom; + m_vEffectMagnitude[1] = fMaxZoom; +} + + +void Actor::AddRotationH( float rot ) +{ + RageQuatMultiply( &DestTweenState().quat, DestTweenState().quat, RageQuatFromH(rot) ); +} + +void Actor::AddRotationP( float rot ) +{ + RageQuatMultiply( &DestTweenState().quat, DestTweenState().quat, RageQuatFromP(rot) ); +} + +void Actor::AddRotationR( float rot ) +{ + RageQuatMultiply( &DestTweenState().quat, DestTweenState().quat, RageQuatFromR(rot) ); +} + +void Actor::RunCommands( const LuaReference& cmds, const LuaReference *pParamTable ) +{ + Lua *L = LUA->Get(); + + // function + cmds.PushSelf( L ); + ASSERT( !lua_isnil(L, -1) ); + + // 1st parameter + this->PushSelf( L ); + + // 2nd parameter + if( pParamTable == NULL ) + lua_pushnil( L ); + else + pParamTable->PushSelf( L ); + + // call function with 2 arguments and 0 results + RString sError; + if( !LuaHelpers::RunScriptOnStack(L, sError, 2, 0) ) + LOG->Warn( "Error playing command: %s", sError.c_str() ); + + LUA->Release(L); +} + +float Actor::GetTweenTimeLeft() const +{ + float tot = 0; + + tot += m_fHibernateSecondsLeft; + + for( unsigned i=0; iinfo.m_fTimeLeftInTween; + + return tot; +} + +/* This is a hack to change all tween states while leaving existing tweens alone. + * + * Hmm. Most commands actually act on a TweenStateAndInfo, not the Actor itself. + * Conceptually, it wouldn't be hard to give TweenState a presence in Lua, so + * we can simply say eg. "for x in states(Actor) do x.SetDiffuseColor(c) end". + * However, we'd then have to give every TweenState a userdata in Lua while it's + * being manipulated, which would add overhead ... */ +void Actor::SetGlobalDiffuseColor( RageColor c ) +{ + for( int i=0; i<4; i++ ) // color, not alpha + { + for( unsigned ts = 0; ts < m_Tweens.size(); ++ts ) + { + m_Tweens[ts]->state.diffuse[i].r = c.r; + m_Tweens[ts]->state.diffuse[i].g = c.g; + m_Tweens[ts]->state.diffuse[i].b = c.b; + } + m_current.diffuse[i].r = c.r; + m_current.diffuse[i].g = c.g; + m_current.diffuse[i].b = c.b; + m_start.diffuse[i].r = c.r; + m_start.diffuse[i].g = c.g; + m_start.diffuse[i].b = c.b; + } +} + +void Actor::SetDiffuseColor( RageColor c ) +{ + for( int i=0; i<4; i++ ) + { + DestTweenState().diffuse[i].r = c.r; + DestTweenState().diffuse[i].g = c.g; + DestTweenState().diffuse[i].b = c.b; + } +} + + +void Actor::TweenState::Init() +{ + pos = RageVector3( 0, 0, 0 ); + rotation = RageVector3( 0, 0, 0 ); + quat = RageVector4( 0, 0, 0, 1 ); + scale = RageVector3( 1, 1, 1 ); + fSkewX = 0; + fSkewY = 0; + crop = RectF( 0,0,0,0 ); + fade = RectF( 0,0,0,0 ); + for( int i=0; i<4; i++ ) + diffuse[i] = RageColor( 1, 1, 1, 1 ); + glow = RageColor( 1, 1, 1, 0 ); + aux = 0; +} + +bool Actor::TweenState::operator==( const TweenState &other ) const +{ +#define COMPARE( x ) if( x != other.x ) return false; + COMPARE( pos ); + COMPARE( rotation ); + COMPARE( quat ); + COMPARE( scale ); + COMPARE( fSkewX ); + COMPARE( fSkewY ); + COMPARE( crop ); + COMPARE( fade ); + for( unsigned i=0; iinfo; + TI.m_sCommandName = sCommandName; +} + +void Actor::QueueMessage( const RString& sMessageName ) +{ + // Hack: use "!" as a marker to broadcast a command, instead of playing a + // command, so we don't have to add yet another element to every tween + // state for this rarely-used command. + BeginTweening( 0, TWEEN_LINEAR ); + TweenInfo &TI = m_Tweens.back()->info; + TI.m_sCommandName = "!" + sMessageName; +} + +void Actor::AddCommand( const RString &sCmdName, apActorCommands apac ) +{ + if( HasCommand(sCmdName) ) + { + RString sWarning = GetLineage()+"'s command '"+sCmdName+"' defined twice"; + Dialog::OK( sWarning, "COMMAND_DEFINED_TWICE" ); + } + + RString sMessage; + if( GetMessageNameFromCommandName(sCmdName, sMessage) ) + { + SubscribeToMessage( sMessage ); + m_mapNameToCommands[sMessage] = apac; // sCmdName w/o "Message" at the end + } + else + { + m_mapNameToCommands[sCmdName] = apac; + } +} + +bool Actor::HasCommand( const RString &sCmdName ) const +{ + return GetCommand(sCmdName) != NULL; +} + +const apActorCommands *Actor::GetCommand( const RString &sCommandName ) const +{ + map::const_iterator it = m_mapNameToCommands.find( sCommandName ); + if( it == m_mapNameToCommands.end() ) + return NULL; + return &it->second; +} + +void Actor::HandleMessage( const Message &msg ) +{ + PlayCommandNoRecurse( msg ); +} + +void Actor::PlayCommandNoRecurse( const Message &msg ) +{ + const apActorCommands *pCmd = GetCommand( msg.GetName() ); + if( pCmd != NULL ) + RunCommands( *pCmd, &msg.GetParamTable() ); +} + +void Actor::PushContext( lua_State *L ) +{ + // self.ctx should already exist + m_pLuaInstance->PushSelf( L ); + lua_getfield( L, -1, "ctx" ); + lua_remove( L, -2 ); +} + +void Actor::SetParent( Actor *pParent ) +{ + m_pParent = pParent; + + Lua *L = LUA->Get(); + int iTop = lua_gettop( L ); + + this->PushContext( L ); + lua_pushstring( L, "__index" ); + pParent->PushContext( L ); + lua_settable( L, -3 ); + + lua_settop( L, iTop ); + LUA->Release( L ); +} + +Actor::TweenInfo::TweenInfo() +{ + m_pTween = NULL; +} + +Actor::TweenInfo::~TweenInfo() +{ + delete m_pTween; +} + +Actor::TweenInfo::TweenInfo( const TweenInfo &cpy ) +{ + m_pTween = NULL; + *this = cpy; +} + +Actor::TweenInfo &Actor::TweenInfo::operator=( const TweenInfo &rhs ) +{ + delete m_pTween; + m_pTween = (rhs.m_pTween? rhs.m_pTween->Copy():NULL); + m_fTimeLeftInTween = rhs.m_fTimeLeftInTween; + m_fTweenTime = rhs.m_fTweenTime; + m_sCommandName = rhs.m_sCommandName; + return *this; +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the Actor. */ +class LunaActor : public Luna +{ +public: + static int name( T* p, lua_State *L ) { p->SetName(SArg(1)); return 0; } + static int sleep( T* p, lua_State *L ) { p->Sleep(FArg(1)); return 0; } + static int linear( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_LINEAR); return 0; } + static int accelerate( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_ACCELERATE); return 0; } + static int decelerate( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_DECELERATE); return 0; } + static int spring( T* p, lua_State *L ) { p->BeginTweening(FArg(1),TWEEN_SPRING); return 0; } + static int tween( T* p, lua_State *L ) + { + ITween *pTween = ITween::CreateFromStack( L, 2 ); + p->BeginTweening( FArg(1), pTween ); + return 0; + } + static int stoptweening( T* p, lua_State *L ) { p->StopTweening(); return 0; } + static int finishtweening( T* p, lua_State *L ) { p->FinishTweening(); return 0; } + static int hurrytweening( T* p, lua_State *L ) { p->HurryTweening(FArg(1)); return 0; } + static int GetTweenTimeLeft( T* p, lua_State *L ) { lua_pushnumber( L, p->GetTweenTimeLeft() ); return 1; } + static int x( T* p, lua_State *L ) { p->SetX(FArg(1)); return 0; } + static int y( T* p, lua_State *L ) { p->SetY(FArg(1)); return 0; } + static int z( T* p, lua_State *L ) { p->SetZ(FArg(1)); return 0; } + static int addx( T* p, lua_State *L ) { p->AddX(FArg(1)); return 0; } + static int addy( T* p, lua_State *L ) { p->AddY(FArg(1)); return 0; } + static int addz( T* p, lua_State *L ) { p->AddZ(FArg(1)); return 0; } + static int zoom( T* p, lua_State *L ) { p->SetZoom(FArg(1)); return 0; } + static int zoomx( T* p, lua_State *L ) { p->SetZoomX(FArg(1)); return 0; } + static int zoomy( T* p, lua_State *L ) { p->SetZoomY(FArg(1)); return 0; } + static int zoomz( T* p, lua_State *L ) { p->SetZoomZ(FArg(1)); return 0; } + static int zoomto( T* p, lua_State *L ) { p->ZoomTo(FArg(1), FArg(2)); return 0; } + static int zoomtowidth( T* p, lua_State *L ) { p->ZoomToWidth(FArg(1)); return 0; } + static int zoomtoheight( T* p, lua_State *L ) { p->ZoomToHeight(FArg(1)); return 0; } + static int setsize( T* p, lua_State *L ) { p->SetWidth(FArg(1)); p->SetHeight(FArg(2)); return 0; } + static int SetWidth( T* p, lua_State *L ) { p->SetWidth(FArg(1)); return 0; } + static int SetHeight( T* p, lua_State *L ) { p->SetHeight(FArg(1)); return 0; } + static int basealpha( T* p, lua_State *L ) { p->SetBaseAlpha(FArg(1)); return 0; } + static int basezoom( T* p, lua_State *L ) { p->SetBaseZoom(FArg(1)); return 0; } + static int basezoomx( T* p, lua_State *L ) { p->SetBaseZoomX(FArg(1)); return 0; } + static int basezoomy( T* p, lua_State *L ) { p->SetBaseZoomY(FArg(1)); return 0; } + static int basezoomz( T* p, lua_State *L ) { p->SetBaseZoomZ(FArg(1)); return 0; } + static int stretchto( T* p, lua_State *L ) { p->StretchTo( RectF(FArg(1),FArg(2),FArg(3),FArg(4)) ); return 0; } + static int cropleft( T* p, lua_State *L ) { p->SetCropLeft(FArg(1)); return 0; } + static int croptop( T* p, lua_State *L ) { p->SetCropTop(FArg(1)); return 0; } + static int cropright( T* p, lua_State *L ) { p->SetCropRight(FArg(1)); return 0; } + static int cropbottom( T* p, lua_State *L ) { p->SetCropBottom(FArg(1)); return 0; } + static int fadeleft( T* p, lua_State *L ) { p->SetFadeLeft(FArg(1)); return 0; } + static int fadetop( T* p, lua_State *L ) { p->SetFadeTop(FArg(1)); return 0; } + static int faderight( T* p, lua_State *L ) { p->SetFadeRight(FArg(1)); return 0; } + static int fadebottom( T* p, lua_State *L ) { p->SetFadeBottom(FArg(1)); return 0; } + static int diffuse( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuse( c ); return 0; } + static int diffuseupperleft( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseUpperLeft( c ); return 0; } + static int diffuseupperright( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseUpperRight( c ); return 0; } + static int diffuselowerleft( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseLowerLeft( c ); return 0; } + static int diffuselowerright( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseLowerRight( c ); return 0; } + static int diffuseleftedge( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseLeftEdge( c ); return 0; } + static int diffuserightedge( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseRightEdge( c ); return 0; } + static int diffusetopedge( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseTopEdge( c ); return 0; } + static int diffusebottomedge( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseBottomEdge( c ); return 0; } + static int diffusealpha( T* p, lua_State *L ) { p->SetDiffuseAlpha(FArg(1)); return 0; } + static int diffusecolor( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseColor( c ); return 0; } + static int glow( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetGlow( c ); return 0; } + static int aux( T* p, lua_State *L ) { p->SetAux( FArg(1) ); return 0; } + static int getaux( T* p, lua_State *L ) { lua_pushnumber( L, p->GetAux() ); return 1; } + static int rotationx( T* p, lua_State *L ) { p->SetRotationX(FArg(1)); return 0; } + static int rotationy( T* p, lua_State *L ) { p->SetRotationY(FArg(1)); return 0; } + static int rotationz( T* p, lua_State *L ) { p->SetRotationZ(FArg(1)); return 0; } + static int addrotationx( T* p, lua_State *L ) { p->AddRotationX(FArg(1)); return 0; } + static int addrotationy( T* p, lua_State *L ) { p->AddRotationY(FArg(1)); return 0; } + static int addrotationz( T* p, lua_State *L ) { p->AddRotationZ(FArg(1)); return 0; } + static int getrotation( T* p, lua_State *L ) { lua_pushnumber(L, p->GetRotationX()); lua_pushnumber(L, p->GetRotationY()); lua_pushnumber(L, p->GetRotationZ()); return 3; } + static int baserotationx( T* p, lua_State *L ) { p->SetBaseRotationX(FArg(1)); return 0; } + static int baserotationy( T* p, lua_State *L ) { p->SetBaseRotationY(FArg(1)); return 0; } + static int baserotationz( T* p, lua_State *L ) { p->SetBaseRotationZ(FArg(1)); return 0; } + static int skewx( T* p, lua_State *L ) { p->SetSkewX(FArg(1)); return 0; } + static int skewy( T* p, lua_State *L ) { p->SetSkewY(FArg(1)); return 0; } + static int heading( T* p, lua_State *L ) { p->AddRotationH(FArg(1)); return 0; } + static int pitch( T* p, lua_State *L ) { p->AddRotationP(FArg(1)); return 0; } + static int roll( T* p, lua_State *L ) { p->AddRotationR(FArg(1)); return 0; } + static int shadowlength( T* p, lua_State *L ) { p->SetShadowLength(FArg(1)); return 0; } + static int shadowlengthx( T* p, lua_State *L ) { p->SetShadowLengthX(FArg(1)); return 0; } + static int shadowlengthy( T* p, lua_State *L ) { p->SetShadowLengthY(FArg(1)); return 0; } + static int shadowcolor( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetShadowColor( c ); return 0; } + static int horizalign( T* p, lua_State *L ) { p->SetHorizAlign(Enum::Check(L, 1)); return 0; } + static int vertalign( T* p, lua_State *L ) { p->SetVertAlign(Enum::Check(L, 1)); return 0; } + static int halign( T* p, lua_State *L ) { p->SetHorizAlign(FArg(1)); return 0; } + static int valign( T* p, lua_State *L ) { p->SetVertAlign(FArg(1)); return 0; } + static int diffuseblink( T* p, lua_State *L ) { p->SetEffectDiffuseBlink(); return 0; } + static int diffuseshift( T* p, lua_State *L ) { p->SetEffectDiffuseShift(); return 0; } + static int diffuseramp( T* p, lua_State *L ) { p->SetEffectDiffuseRamp(); return 0; } + static int glowblink( T* p, lua_State *L ) { p->SetEffectGlowBlink(); return 0; } + static int glowshift( T* p, lua_State *L ) { p->SetEffectGlowShift(); return 0; } + static int glowramp( T* p, lua_State *L ) { p->SetEffectGlowRamp(); return 0; } + static int rainbow( T* p, lua_State *L ) { p->SetEffectRainbow(); return 0; } + static int wag( T* p, lua_State *L ) { p->SetEffectWag(); return 0; } + static int bounce( T* p, lua_State *L ) { p->SetEffectBounce(); return 0; } + static int bob( T* p, lua_State *L ) { p->SetEffectBob(); return 0; } + static int pulse( T* p, lua_State *L ) { p->SetEffectPulse(); return 0; } + static int spin( T* p, lua_State *L ) { p->SetEffectSpin(); return 0; } + static int vibrate( T* p, lua_State *L ) { p->SetEffectVibrate(); return 0; } + static int stopeffect( T* p, lua_State *L ) { p->StopEffect(); return 0; } + static int effectcolor1( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetEffectColor1( c ); return 0; } + static int effectcolor2( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetEffectColor2( c ); return 0; } + static int effectperiod( T* p, lua_State *L ) { p->SetEffectPeriod(FArg(1)); return 0; } + static int effecttiming( T* p, lua_State *L ) { p->SetEffectTiming(FArg(1),FArg(2),FArg(3),FArg(4)); return 0; } + static int effectoffset( T* p, lua_State *L ) { p->SetEffectOffset(FArg(1)); return 0; } + static int effectclock( T* p, lua_State *L ) { p->SetEffectClockString(SArg(1)); return 0; } + static int effectmagnitude( T* p, lua_State *L ) { p->SetEffectMagnitude( RageVector3(FArg(1),FArg(2),FArg(3)) ); return 0; } + static int geteffectmagnitude( T* p, lua_State *L ) { RageVector3 v = p->GetEffectMagnitude(); lua_pushnumber(L, v[0]); lua_pushnumber(L, v[1]); lua_pushnumber(L, v[2]); return 3; } + static int scaletocover( T* p, lua_State *L ) { p->ScaleToCover( RectF(FArg(1), FArg(2), FArg(3), FArg(4)) ); return 0; } + static int scaletofit( T* p, lua_State *L ) { p->ScaleToFitInside( RectF(FArg(1), FArg(2), FArg(3), FArg(4)) ); return 0; } + static int animate( T* p, lua_State *L ) { p->EnableAnimation(BIArg(1)); return 0; } + static int play( T* p, lua_State *L ) { p->EnableAnimation(true); return 0; } + static int pause( T* p, lua_State *L ) { p->EnableAnimation(false); return 0; } + static int setstate( T* p, lua_State *L ) { p->SetState(IArg(1)); return 0; } + static int GetNumStates( T* p, lua_State *L ) { LuaHelpers::Push( L, p->GetNumStates() ); return 1; } + static int texturewrapping( T* p, lua_State *L ) { p->SetTextureWrapping(BIArg(1)); return 0; } + static int SetTextureFiltering( T* p, lua_State *L ) { p->SetTextureFiltering(BArg(1)); return 0; } + static int blend( T* p, lua_State *L ) { p->SetBlendMode( Enum::Check(L, 1) ); return 0; } + static int zbuffer( T* p, lua_State *L ) { p->SetUseZBuffer(BIArg(1)); return 0; } + static int ztest( T* p, lua_State *L ) { p->SetZTestMode((BIArg(1))?ZTEST_WRITE_ON_PASS:ZTEST_OFF); return 0; } + static int ztestmode( T* p, lua_State *L ) { p->SetZTestMode( Enum::Check(L, 1) ); return 0; } + static int zwrite( T* p, lua_State *L ) { p->SetZWrite(BIArg(1)); return 0; } + static int zbias( T* p, lua_State *L ) { p->SetZBias(FArg(1)); return 0; } + static int clearzbuffer( T* p, lua_State *L ) { p->SetClearZBuffer(BIArg(1)); return 0; } + static int backfacecull( T* p, lua_State *L ) { p->SetCullMode((BIArg(1)) ? CULL_BACK : CULL_NONE); return 0; } + static int cullmode( T* p, lua_State *L ) { p->SetCullMode( Enum::Check(L, 1)); return 0; } + static int visible( T* p, lua_State *L ) { p->SetVisible(BIArg(1)); return 0; } + static int hibernate( T* p, lua_State *L ) { p->SetHibernate(FArg(1)); return 0; } + static int draworder( T* p, lua_State *L ) { p->SetDrawOrder(IArg(1)); return 0; } + static int playcommand( T* p, lua_State *L ) + { + if( !lua_istable(L, 2) && !lua_isnoneornil(L, 2) ) + luaL_typerror( L, 2, "table or nil" ); + + LuaReference ParamTable; + lua_pushvalue( L, 2 ); + ParamTable.SetFromStack( L ); + + Message msg( SArg(1), ParamTable ); + p->HandleMessage( msg ); + + return 0; + } + static int queuecommand( T* p, lua_State *L ) { p->QueueCommand(SArg(1)); return 0; } + static int queuemessage( T* p, lua_State *L ) { p->QueueMessage(SArg(1)); return 0; } + static int addcommand( T* p, lua_State *L ) + { + LuaReference *pRef = new LuaReference; + pRef->SetFromStack( L ); + p->AddCommand( SArg(1), apActorCommands(pRef) ); + return 0; + } + static int GetCommand( T* p, lua_State *L ) + { + const apActorCommands *pCommand = p->GetCommand(SArg(1)); + if( pCommand == NULL ) + lua_pushnil( L ); + else + (*pCommand)->PushSelf(L); + + return 1; + } + static int RunCommandsRecursively( T* p, lua_State *L ) + { + luaL_checktype( L, 1, LUA_TFUNCTION ); + if( !lua_istable(L, 2) && !lua_isnoneornil(L, 2) ) + luaL_typerror( L, 2, "table or nil" ); + + LuaReference ref; + lua_pushvalue( L, 1 ); + ref.SetFromStack( L ); + + LuaReference ParamTable; + lua_pushvalue( L, 2 ); + ParamTable.SetFromStack( L ); + + p->RunCommandsRecursively( ref, &ParamTable ); + return 0; + } + + static int GetX( T* p, lua_State *L ) { lua_pushnumber( L, p->GetX() ); return 1; } + static int GetY( T* p, lua_State *L ) { lua_pushnumber( L, p->GetY() ); return 1; } + static int GetZ( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZ() ); return 1; } + static int GetWidth( T* p, lua_State *L ) { lua_pushnumber( L, p->GetUnzoomedWidth() ); return 1; } + static int GetHeight( T* p, lua_State *L ) { lua_pushnumber( L, p->GetUnzoomedHeight() ); return 1; } + static int GetZoomedWidth( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZoomedWidth() ); return 1; } + static int GetZoomedHeight( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZoomedHeight() ); return 1; } + static int GetZoom( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZoom() ); return 1; } + static int GetZoomX( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZoomX() ); return 1; } + static int GetZoomY( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZoomY() ); return 1; } + static int GetZoomZ( T* p, lua_State *L ) { lua_pushnumber( L, p->GetZoomZ() ); return 1; } + static int GetBaseZoomX( T* p, lua_State *L ) { lua_pushnumber( L, p->GetBaseZoomX() ); return 1; } + static int GetBaseZoomY( T* p, lua_State *L ) { lua_pushnumber( L, p->GetBaseZoomY() ); return 1; } + static int GetBaseZoomZ( T* p, lua_State *L ) { lua_pushnumber( L, p->GetBaseZoomZ() ); return 1; } + static int GetRotationX( T* p, lua_State *L ) { lua_pushnumber( L, p->GetRotationX() ); return 1; } + static int GetRotationY( T* p, lua_State *L ) { lua_pushnumber( L, p->GetRotationY() ); return 1; } + static int GetRotationZ( T* p, lua_State *L ) { lua_pushnumber( L, p->GetRotationZ() ); return 1; } + static int GetSecsIntoEffect( T* p, lua_State *L ) { lua_pushnumber( L, p->GetSecsIntoEffect() ); return 1; } + static int GetEffectDelta( T* p, lua_State *L ) { lua_pushnumber( L, p->GetEffectDelta() ); return 1; } + DEFINE_METHOD( GetDiffuse, GetDiffuse() ) + DEFINE_METHOD( GetGlow, GetGlow() ) + static int GetDiffuseAlpha( T* p, lua_State *L ) { lua_pushnumber( L, p->GetDiffuseAlpha() ); return 1; } + static int GetVisible( T* p, lua_State *L ) { lua_pushboolean( L, p->GetVisible() ); return 1; } + static int GetHAlign( T* p, lua_State *L ) { lua_pushnumber( L, p->GetHorizAlign() ); return 1; } + static int GetVAlign( T* p, lua_State *L ) { lua_pushnumber( L, p->GetVertAlign() ); return 1; } + + static int GetName( T* p, lua_State *L ) { lua_pushstring( L, p->GetName() ); return 1; } + static int GetParent( T* p, lua_State *L ) + { + Actor *pParent = p->GetParent(); + if( pParent == NULL ) + lua_pushnil( L ); + else + pParent->PushSelf(L); + return 1; + } + static int Draw( T* p, lua_State *L ) + { + LUA->YieldLua(); + p->Draw(); + LUA->UnyieldLua(); + return 0; + } + + LunaActor() + { + ADD_METHOD( name ); + ADD_METHOD( sleep ); + ADD_METHOD( linear ); + ADD_METHOD( accelerate ); + ADD_METHOD( decelerate ); + ADD_METHOD( spring ); + ADD_METHOD( tween ); + ADD_METHOD( stoptweening ); + ADD_METHOD( finishtweening ); + ADD_METHOD( hurrytweening ); + ADD_METHOD( GetTweenTimeLeft ); + ADD_METHOD( x ); + ADD_METHOD( y ); + ADD_METHOD( z ); + ADD_METHOD( addx ); + ADD_METHOD( addy ); + ADD_METHOD( addz ); + ADD_METHOD( zoom ); + ADD_METHOD( zoomx ); + ADD_METHOD( zoomy ); + ADD_METHOD( zoomz ); + ADD_METHOD( zoomto ); + ADD_METHOD( zoomtowidth ); + ADD_METHOD( zoomtoheight ); + ADD_METHOD( setsize ); + ADD_METHOD( SetWidth ); + ADD_METHOD( SetHeight ); + ADD_METHOD( basealpha ); + ADD_METHOD( basezoom ); + ADD_METHOD( basezoomx ); + ADD_METHOD( basezoomy ); + ADD_METHOD( basezoomz ); + ADD_METHOD( stretchto ); + ADD_METHOD( cropleft ); + ADD_METHOD( croptop ); + ADD_METHOD( cropright ); + ADD_METHOD( cropbottom ); + ADD_METHOD( fadeleft ); + ADD_METHOD( fadetop ); + ADD_METHOD( faderight ); + ADD_METHOD( fadebottom ); + ADD_METHOD( diffuse ); + ADD_METHOD( diffuseupperleft ); + ADD_METHOD( diffuseupperright ); + ADD_METHOD( diffuselowerleft ); + ADD_METHOD( diffuselowerright ); + ADD_METHOD( diffuseleftedge ); + ADD_METHOD( diffuserightedge ); + ADD_METHOD( diffusetopedge ); + ADD_METHOD( diffusebottomedge ); + ADD_METHOD( diffusealpha ); + ADD_METHOD( diffusecolor ); + ADD_METHOD( glow ); + ADD_METHOD( aux ); + ADD_METHOD( getaux ); + ADD_METHOD( rotationx ); + ADD_METHOD( rotationy ); + ADD_METHOD( rotationz ); + ADD_METHOD( addrotationx ); + ADD_METHOD( addrotationy ); + ADD_METHOD( addrotationz ); + ADD_METHOD( getrotation ); + ADD_METHOD( baserotationx ); + ADD_METHOD( baserotationy ); + ADD_METHOD( baserotationz ); + ADD_METHOD( skewx ); + ADD_METHOD( skewy ); + ADD_METHOD( heading ); + ADD_METHOD( pitch ); + ADD_METHOD( roll ); + ADD_METHOD( shadowlength ); + ADD_METHOD( shadowlengthx ); + ADD_METHOD( shadowlengthy ); + ADD_METHOD( shadowcolor ); + ADD_METHOD( horizalign ); + ADD_METHOD( vertalign ); + ADD_METHOD( halign ); + ADD_METHOD( valign ); + ADD_METHOD( diffuseblink ); + ADD_METHOD( diffuseshift ); + ADD_METHOD( diffuseramp ); + ADD_METHOD( glowblink ); + ADD_METHOD( glowshift ); + ADD_METHOD( glowramp ); + ADD_METHOD( rainbow ); + ADD_METHOD( wag ); + ADD_METHOD( bounce ); + ADD_METHOD( bob ); + ADD_METHOD( pulse ); + ADD_METHOD( spin ); + ADD_METHOD( vibrate ); + ADD_METHOD( stopeffect ); + ADD_METHOD( effectcolor1 ); + ADD_METHOD( effectcolor2 ); + ADD_METHOD( effectperiod ); + ADD_METHOD( effecttiming ); + ADD_METHOD( effectoffset ); + ADD_METHOD( effectclock ); + ADD_METHOD( effectmagnitude ); + ADD_METHOD( geteffectmagnitude ); + ADD_METHOD( scaletocover ); + ADD_METHOD( scaletofit ); + ADD_METHOD( animate ); + ADD_METHOD( play ); + ADD_METHOD( pause ); + ADD_METHOD( setstate ); + ADD_METHOD( GetNumStates ); + ADD_METHOD( texturewrapping ); + ADD_METHOD( SetTextureFiltering ); + ADD_METHOD( blend ); + ADD_METHOD( zbuffer ); + ADD_METHOD( ztest ); + ADD_METHOD( ztestmode ); + ADD_METHOD( zwrite ); + ADD_METHOD( zbias ); + ADD_METHOD( clearzbuffer ); + ADD_METHOD( backfacecull ); + ADD_METHOD( cullmode ); + ADD_METHOD( visible ); + ADD_METHOD( hibernate ); + ADD_METHOD( draworder ); + ADD_METHOD( playcommand ); + ADD_METHOD( queuecommand ); + ADD_METHOD( queuemessage ); + ADD_METHOD( addcommand ); + ADD_METHOD( GetCommand ); + ADD_METHOD( RunCommandsRecursively ); + + ADD_METHOD( GetX ); + ADD_METHOD( GetY ); + ADD_METHOD( GetZ ); + ADD_METHOD( GetWidth ); + ADD_METHOD( GetHeight ); + ADD_METHOD( GetZoomedWidth ); + ADD_METHOD( GetZoomedHeight ); + ADD_METHOD( GetZoom ); + ADD_METHOD( GetZoomX ); + ADD_METHOD( GetZoomY ); + ADD_METHOD( GetZoomZ ); + ADD_METHOD( GetRotationX ); + ADD_METHOD( GetRotationY ); + ADD_METHOD( GetRotationZ ); + ADD_METHOD( GetBaseZoomX ); + ADD_METHOD( GetBaseZoomY ); + ADD_METHOD( GetBaseZoomZ ); + ADD_METHOD( GetSecsIntoEffect ); + ADD_METHOD( GetEffectDelta ); + ADD_METHOD( GetDiffuse ); + ADD_METHOD( GetDiffuseAlpha ); + ADD_METHOD( GetGlow ); + ADD_METHOD( GetVisible ); + ADD_METHOD( GetHAlign ); + ADD_METHOD( GetVAlign ); + + ADD_METHOD( GetName ); + ADD_METHOD( GetParent ); + + ADD_METHOD( Draw ); + } +}; + +LUA_REGISTER_INSTANCED_BASE_CLASS( Actor ) +// lua end + + +/* + * (c) 2001-2004 Chris Danford + * 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/src/Actors/Actor.h b/src/Actors/Actor.h new file mode 100644 index 0000000000..919d4994f7 --- /dev/null +++ b/src/Actors/Actor.h @@ -0,0 +1,755 @@ +#ifndef ACTOR_H +#define ACTOR_H + +#include "PlayerNumber.h" +#include "RageTypes.h" +#include "RageUtil_AutoPtr.h" +#include "LuaReference.h" +#include "EnumHelper.h" +#include +class XNode; +struct lua_State; +class LuaClass; +#include "MessageManager.h" +#include "Tween.h" + +typedef AutoPtrCopyOnWrite apActorCommands; + +/** @brief The background layer. */ +#define DRAW_ORDER_BEFORE_EVERYTHING -200 +/** @brief The underlay layer. */ +#define DRAW_ORDER_UNDERLAY -100 +/** @brief The decorations layer. */ +#define DRAW_ORDER_DECORATIONS 0 +/** @brief The overlay layer. + * + * Normal screen elements go here. */ +#define DRAW_ORDER_OVERLAY +100 +/** @brief The transitions layer. */ +#define DRAW_ORDER_TRANSITIONS +200 +/** @brief The over everything layer. */ +#define DRAW_ORDER_AFTER_EVERYTHING +300 + +/** @brief The different horizontal alignments. */ +enum HorizAlign +{ + HorizAlign_Left, /**< Align to the left. */ + HorizAlign_Center, /**< Align to the center. */ + HorizAlign_Right, /**< Align to the right. */ + NUM_HorizAlign, /**< The number of horizontal alignments. */ + HorizAlign_Invalid +}; +LuaDeclareType( HorizAlign ); + +/** @brief The different vertical alignments. */ +enum VertAlign +{ + VertAlign_Top, /**< Align to the top. */ + VertAlign_Middle, /**< Align to the middle. */ + VertAlign_Bottom, /**< Align to the bottom. */ + NUM_VertAlign, /**< The number of vertical alignments. */ + VertAlign_Invalid +}; +LuaDeclareType( VertAlign ); + +/** @brief The left horizontal alignment constant. */ +#define align_left 0.0f +/** @brief The center horizontal alignment constant. */ +#define align_center 0.5f +/** @brief The right horizontal alignment constant. */ +#define align_right 1.0f +/** @brief The top vertical alignment constant. */ +#define align_top 0.0f +/** @brief The middle vertical alignment constant. */ +#define align_middle 0.5f +/** @brief The bottom vertical alignment constant. */ +#define align_bottom 1.0f + +// ssc futures: +/* +enum EffectAction +{ + EffectAction_None, // no_effect + // [Diffuse] + EffectAction_DiffuseBlink, // diffuse_blink + EffectAction_DiffuseShift, // diffuse_shift + EffectAction_DiffuseRamp, // diffuse_ramp + EffectAction_Rainbow, // rainbow + // [Glow] + EffectAction_GlowBlink, // glow_blink + EffectAction_GlowShift, // glow_shift + EffectAction_GlowRamp, // glow_ramp + // [Translate] + EffectAction_Bob, + EffectAction_Bounce, + EffectAction_Vibrate, + // [Rotate] + EffectAction_Spin, + EffectAction_Wag, + // [Zoom] + EffectAction_Pulse, + NUM_EffectAction, + EffectAction_Invalid +}; +LuaDeclareType( EffectAction ); +*/ + +/** @brief Base class for all objects that appear on the screen. */ +class Actor : public MessageSubscriber +{ +public: + /** @brief Set up the Actor with its initial settings. */ + Actor(); + /** + * @brief Copy a new Actor to the old one. + * @param cpy the new Actor to use in place of this one. */ + Actor( const Actor &cpy ); + virtual ~Actor(); + virtual Actor *Copy() const; + virtual void InitState(); + virtual void LoadFromNode( const XNode* pNode ); + + static void SetBGMTime( float fTime, float fBeat, float fTimeNoOffset, float fBeatNoOffset ); + static void SetPlayerBGMBeat( PlayerNumber pn, float fBeat, float fBeatNoOffset ); + static void SetBGMLight( int iLightNumber, float fCabinetLights ); + + /** + * @brief The list of the different effects. + * + * todo: split out into diffuse effects and translation effects, or + * create an effect stack instead. -aj */ + enum Effect { no_effect, + diffuse_blink, diffuse_shift, diffuse_ramp, + glow_blink, glow_shift, glow_ramp, rainbow, + wag, bounce, bob, pulse, spin, vibrate + }; + + /** @brief Various values an Actor's effect can be tied to. */ + enum EffectClock + { + CLOCK_TIMER, + CLOCK_TIMER_GLOBAL, + CLOCK_BGM_TIME, + CLOCK_BGM_BEAT, + CLOCK_BGM_TIME_NO_OFFSET, + CLOCK_BGM_BEAT_NO_OFFSET, + CLOCK_BGM_BEAT_PLAYER1, + CLOCK_BGM_BEAT_PLAYER2, + CLOCK_LIGHT_1 = 1000, + CLOCK_LIGHT_LAST = 1100, + NUM_CLOCKS + }; + + /* + * @brief What type of Effect this is. + * + * This is an internal enum for checking if an effect can be run; + * You can't have more than one of most EffectTypes in the Effect list. (You + * might be able to have mutliple EffectType_Translates; not sure yet.) -aj */ + /* + enum EffectType { + EffectType_Diffuse, + EffectType_Glow, + EffectType_Translate, + EffectType_Rotate, + EffectType_Zoom, + NUM_EffectType, + EffectType_Invalid + }; + */ + + // todo: use this instead of the Effect enum -aj + /* + // This is similar to Attributes in BitmapText as far as implementation. + struct Effect + { + Effect() : m_Action(EffectAction_None), m_Type(EffectType_Invalid), m_fSecsIntoEffect(0), + m_fEffectDelta(0), m_fEffectRampUp(0.5f), m_fEffectHoldAtHalf(0), + m_fEffectRampDown(0.5f), m_fEffectHoldAtZero(0), m_fEffectOffset(0), + m_EffectClock(CLOCK_TIMER), m_vEffectMagnitude(RageVector3(0,0,10)), + m_effectColor1(RageColor(1,1,1,1)), m_effectColor2(RageColor(1,1,1,1)) + { } + + RString m_sName; // friendly name + EffectAction m_Action; // replaces the old Effect enum + EffectType m_Type; // determined by EffectAction + float m_fSecsIntoEffect; + float m_fEffectDelta; + RageColor m_EffectColor1; + RageColor m_EffectColor2; + RageVector3 m_vEffectMagnitude; + EffectClock m_EffectClock; + // units depend on m_EffectClock + float m_fEffectRampUp; + float m_fEffectHoldAtHalf; + float m_fEffectRampDown; + float m_fEffectHoldAtZero; + float m_fEffectOffset; + }; + */ + + /** + * @brief The present state for the Tween. + */ + struct TweenState + { + void Init(); + static void MakeWeightedAverage( TweenState& average_out, const TweenState& ts1, const TweenState& ts2, float fPercentBetween ); + bool operator==( const TweenState &other ) const; + bool operator!=( const TweenState &other ) const { return !operator==(other); } + + // start and end position for tweening + RageVector3 pos; + RageVector3 rotation; + RageVector4 quat; + RageVector3 scale; + float fSkewX, fSkewY; + /** + * @brief The amount of cropping involved. + * + * If 0, there is no cropping. If 1, it's fully cropped. */ + RectF crop; + /** + * @brief The amount of fading involved. + * + * If 0, there is no fade. If 1, it's fully faded. */ + RectF fade; + /** + * @brief Four values making up the diffuse in this TweenState. + * + * 0 = UpperLeft, 1 = UpperRight, 2 = LowerLeft, 3 = LowerRight */ + RageColor diffuse[4]; + /** @brief The glow color for this TweenState. */ + RageColor glow; + /** @brief A magical value that nobody really knows the use for. ;) */ + float aux; + }; + + /** + * @brief Calls multiple functions for drawing the Actors. + * + * It calls the following in order: + * -# EarlyAbortDraw + * -# BeginDraw + * -# DrawPrimitives + * -# EndDraw + */ + void Draw(); + /** + * @brief Allow the Actor to be aborted early. + * + * Subclasses may wish to overwrite this to allow for + * aborted actors. + * @return false, as by default Actors shouldn't be aborted on drawing. */ + virtual bool EarlyAbortDraw() const { return false; } + /** @brief Start the drawing and push the transform on the world matrix stack. */ + virtual void BeginDraw(); + /** + * @brief Set the global rendering states of this Actor. + * + * This should be called at the beginning of an Actor's DrawPrimitives() call. */ + virtual void SetGlobalRenderStates(); + /** + * @brief Set the texture rendering states of this Actor. + * + * This should be called after setting a texture for the Actor. */ + virtual void SetTextureRenderStates(); + /** + * @brief Draw the primitives of the Actor. + * + * Derivative classes should override this function. */ + virtual void DrawPrimitives() {}; + /** @brief Pop the transform from the world matrix stack. */ + virtual void EndDraw(); + + // TODO: make Update non virtual and change all classes to override UpdateInternal + // instead. + bool IsFirstUpdate() const; + virtual void Update( float fDeltaTime ); // this can short circuit UpdateInternal + virtual void UpdateInternal( float fDeltaTime ); // override this + void UpdateTweening( float fDeltaTime ); + + /** + * @brief Retrieve the Actor's name. + * @return the Actor's name. */ + const RString &GetName() const { return m_sName; } + /** + * @brief Set the Actor's name to a new one. + * @param sName the new name for the Actor. */ + virtual void SetName( const RString &sName ) { m_sName = sName; } + /** + * @brief Give this Actor a new parent. + * @param pParent the new parent Actor. */ + void SetParent( Actor *pParent ); + /** + * @brief Retrieve the Actor's parent. + * @return the Actor's parent. */ + Actor *GetParent() { return m_pParent; } + /** + * @brief Retrieve the Actor's lineage. + * @return the Actor's lineage. */ + RString GetLineage() const; + + /** + * @brief Retrieve the Actor's x position. + * @return the Actor's x position. */ + float GetX() const { return m_current.pos.x; }; + /** + * @brief Retrieve the Actor's y position. + * @return the Actor's y position. */ + float GetY() const { return m_current.pos.y; }; + /** + * @brief Retrieve the Actor's z position. + * @return the Actor's z position. */ + float GetZ() const { return m_current.pos.z; }; + float GetDestX() const { return DestTweenState().pos.x; }; + float GetDestY() const { return DestTweenState().pos.y; }; + float GetDestZ() const { return DestTweenState().pos.z; }; + void SetX( float x ) { DestTweenState().pos.x = x; }; + void SetY( float y ) { DestTweenState().pos.y = y; }; + void SetZ( float z ) { DestTweenState().pos.z = z; }; + void SetXY( float x, float y ) { DestTweenState().pos.x = x; DestTweenState().pos.y = y; }; + /** + * @brief Add to the x position of this Actor. + * @param x the amount to add to the Actor's x position. */ + void AddX( float x ) { SetX( GetDestX()+x ); } + /** + * @brief Add to the y position of this Actor. + * @param y the amount to add to the Actor's y position. */ + void AddY( float y ) { SetY( GetDestY()+y ); } + /** + * @brief Add to the z position of this Actor. + * @param z the amount to add to the Actor's z position. */ + void AddZ( float z ) { SetZ( GetDestZ()+z ); } + + // height and width vary depending on zoom + float GetUnzoomedWidth() const { return m_size.x; } + float GetUnzoomedHeight() const { return m_size.y; } + float GetZoomedWidth() const { return m_size.x * m_baseScale.x * DestTweenState().scale.x; } + float GetZoomedHeight() const { return m_size.y * m_baseScale.y * DestTweenState().scale.y; } + void SetWidth( float width ) { m_size.x = width; } + void SetHeight( float height ) { m_size.y = height; } + + // Base values + float GetBaseZoomX() const { return m_baseScale.x; } + void SetBaseZoomX( float zoom ) { m_baseScale.x = zoom; } + float GetBaseZoomY() const { return m_baseScale.y; } + void SetBaseZoomY( float zoom ) { m_baseScale.y = zoom; } + float GetBaseZoomZ() const { return m_baseScale.z; } + void SetBaseZoomZ( float zoom ) { m_baseScale.z = zoom; } + void SetBaseZoom( float zoom ) { m_baseScale = RageVector3(zoom,zoom,zoom); } + void SetBaseRotationX( float rot ) { m_baseRotation.x = rot; } + void SetBaseRotationY( float rot ) { m_baseRotation.y = rot; } + void SetBaseRotationZ( float rot ) { m_baseRotation.z = rot; } + void SetBaseRotation( const RageVector3 &rot ) { m_baseRotation = rot; } + virtual void SetBaseAlpha( float fAlpha ) { m_fBaseAlpha = fAlpha; } + + /** + * @brief Retrieve the general zoom factor, using the x coordinate of the Actor. + * + * Note that this is not accurate in some cases. + * @return the zoom factor for the x coordinate of the Actor. */ + float GetZoom() const { return DestTweenState().scale.x; } + /** + * @brief Retrieve the zoom factor for the x coordinate of the Actor. + * @return the zoom factor for the x coordinate of the Actor. */ + float GetZoomX() const { return DestTweenState().scale.x; } + /** + * @brief Retrieve the zoom factor for the y coordinate of the Actor. + * @return the zoom factor for the y coordinate of the Actor. */ + float GetZoomY() const { return DestTweenState().scale.y; } + /** + * @brief Retrieve the zoom factor for the z coordinate of the Actor. + * @return the zoom factor for the z coordinate of the Actor. */ + float GetZoomZ() const { return DestTweenState().scale.z; } + /** + * @brief Set the zoom factor for all dimensions of the Actor. + * @param zoom the zoom factor for all dimensions. */ + void SetZoom( float zoom ) + { + DestTweenState().scale.x = zoom; + DestTweenState().scale.y = zoom; + DestTweenState().scale.z = zoom; + } + /** + * @brief Set the zoom factor for the x dimension of the Actor. + * @param zoom the zoom factor for the x dimension. */ + void SetZoomX( float zoom ) { DestTweenState().scale.x = zoom; } + /** + * @brief Set the zoom factor for the y dimension of the Actor. + * @param zoom the zoom factor for the y dimension. */ + void SetZoomY( float zoom ) { DestTweenState().scale.y = zoom; } + /** + * @brief Set the zoom factor for the z dimension of the Actor. + * @param zoom the zoom factor for the z dimension. */ + void SetZoomZ( float zoom ) { DestTweenState().scale.z = zoom; } + void ZoomTo( float fX, float fY ) { ZoomToWidth(fX); ZoomToHeight(fY); } + void ZoomToWidth( float zoom ) { SetZoomX( zoom / GetUnzoomedWidth() ); } + void ZoomToHeight( float zoom ) { SetZoomY( zoom / GetUnzoomedHeight() ); } + + float GetRotationX() const { return DestTweenState().rotation.x; } + float GetRotationY() const { return DestTweenState().rotation.y; } + float GetRotationZ() const { return DestTweenState().rotation.z; } + void SetRotationX( float rot ) { DestTweenState().rotation.x = rot; } + void SetRotationY( float rot ) { DestTweenState().rotation.y = rot; } + void SetRotationZ( float rot ) { DestTweenState().rotation.z = rot; } + // added in StepNXA, now available in sm-ssc: + void AddRotationX( float rot ) { DestTweenState().rotation.x += rot; }; + void AddRotationY( float rot ) { DestTweenState().rotation.y += rot; }; + void AddRotationZ( float rot ) { DestTweenState().rotation.z += rot; }; + // and these were normally in SM: + void AddRotationH( float rot ); + void AddRotationP( float rot ); + void AddRotationR( float rot ); + + void SetSkewX( float fAmount ) { DestTweenState().fSkewX = fAmount; } + float GetSkewX( float fAmount ) const { return DestTweenState().fSkewX; } + void SetSkewY( float fAmount ) { DestTweenState().fSkewY = fAmount; } + float GetSkewY( float fAmount ) const { return DestTweenState().fSkewY; } + + float GetCropLeft() const { return DestTweenState().crop.left; } + float GetCropTop() const { return DestTweenState().crop.top; } + float GetCropRight() const { return DestTweenState().crop.right; } + float GetCropBottom() const { return DestTweenState().crop.bottom; } + void SetCropLeft( float percent ) { DestTweenState().crop.left = percent; } + void SetCropTop( float percent ) { DestTweenState().crop.top = percent; } + void SetCropRight( float percent ) { DestTweenState().crop.right = percent; } + void SetCropBottom( float percent ) { DestTweenState().crop.bottom = percent; } + + void SetFadeLeft( float percent ) { DestTweenState().fade.left = percent; } + void SetFadeTop( float percent ) { DestTweenState().fade.top = percent; } + void SetFadeRight( float percent ) { DestTweenState().fade.right = percent; } + void SetFadeBottom( float percent ) { DestTweenState().fade.bottom = percent; } + + void SetGlobalDiffuseColor( RageColor c ); + + virtual void SetDiffuse( RageColor c ) { for(int i=0; i<4; i++) DestTweenState().diffuse[i] = c; }; + virtual void SetDiffuseAlpha( float f ) { for(int i = 0; i < 4; ++i) { RageColor c = GetDiffuses( i ); c.a = f; SetDiffuses( i, c ); } } + float GetCurrentDiffuseAlpha() const { return m_current.diffuse[0].a; } + void SetDiffuseColor( RageColor c ); + void SetDiffuses( int i, RageColor c ) { DestTweenState().diffuse[i] = c; }; + void SetDiffuseUpperLeft( RageColor c ) { DestTweenState().diffuse[0] = c; }; + void SetDiffuseUpperRight( RageColor c ) { DestTweenState().diffuse[1] = c; }; + void SetDiffuseLowerLeft( RageColor c ) { DestTweenState().diffuse[2] = c; }; + void SetDiffuseLowerRight( RageColor c ) { DestTweenState().diffuse[3] = c; }; + void SetDiffuseTopEdge( RageColor c ) { DestTweenState().diffuse[0] = DestTweenState().diffuse[1] = c; }; + void SetDiffuseRightEdge( RageColor c ) { DestTweenState().diffuse[1] = DestTweenState().diffuse[3] = c; }; + void SetDiffuseBottomEdge( RageColor c ) { DestTweenState().diffuse[2] = DestTweenState().diffuse[3] = c; }; + void SetDiffuseLeftEdge( RageColor c ) { DestTweenState().diffuse[0] = DestTweenState().diffuse[2] = c; }; + RageColor GetDiffuse() const { return DestTweenState().diffuse[0]; }; + RageColor GetDiffuses( int i ) const { return DestTweenState().diffuse[i]; }; + float GetDiffuseAlpha() const { return DestTweenState().diffuse[0].a; }; + void SetGlow( RageColor c ) { DestTweenState().glow = c; }; + RageColor GetGlow() const { return DestTweenState().glow; }; + + void SetAux( float f ) { DestTweenState().aux = f; } + float GetAux() const { return m_current.aux; } + + void BeginTweening( float time, ITween *pInterp ); + void BeginTweening( float time, TweenType tt = TWEEN_LINEAR ); + void StopTweening(); + void Sleep( float time ); + void QueueCommand( const RString& sCommandName ); + void QueueMessage( const RString& sMessageName ); + virtual void FinishTweening(); + virtual void HurryTweening( float factor ); + // Let ActorFrame and BGAnimation override + virtual float GetTweenTimeLeft() const; // Amount of time until all tweens have stopped + TweenState& DestTweenState() // where Actor will end when its tween finish + { + if( m_Tweens.empty() ) // not tweening + return m_current; + else + return m_Tweens.back()->state; + } + const TweenState& DestTweenState() const { return const_cast(this)->DestTweenState(); } + + /** @brief How do we handle stretching the Actor? */ + enum StretchType + { + fit_inside, /**< Have the Actor fit inside its parent, using the smaller zoom. */ + cover /**< Have the Actor cover its parent, using the larger zoom. */ + }; + + void ScaleToCover( const RectF &rect ) { ScaleTo( rect, cover ); } + void ScaleToFitInside( const RectF &rect ) { ScaleTo( rect, fit_inside); }; + void ScaleTo( const RectF &rect, StretchType st ); + + void StretchTo( const RectF &rect ); + + // Alignment settings. These need to be virtual for BitmapText + virtual void SetHorizAlign( float f ) { m_fHorizAlign = f; } + virtual void SetVertAlign( float f ) { m_fVertAlign = f; } + void SetHorizAlign( HorizAlign ha ) { SetHorizAlign( (ha == HorizAlign_Left)? 0.0f: (ha == HorizAlign_Center)? 0.5f: +1.0f ); } + void SetVertAlign( VertAlign va ) { SetVertAlign( (va == VertAlign_Top)? 0.0f: (va == VertAlign_Middle)? 0.5f: +1.0f ); } + virtual float GetHorizAlign() { return m_fHorizAlign; } + virtual float GetVertAlign() { return m_fVertAlign; } + + // effects +#if defined(SSC_FUTURES) + void StopEffects(); + Effect GetEffect( int i ) const { return m_Effects[i]; } +#else + void StopEffect() { m_Effect = no_effect; } + Effect GetEffect() const { return m_Effect; } +#endif + float GetSecsIntoEffect() const { return m_fSecsIntoEffect; } + float GetEffectDelta() const { return m_fEffectDelta; } + + // todo: account for SSC_FUTURES by adding an effect as an arg to each one -aj + void SetEffectColor1( RageColor c ) { m_effectColor1 = c; } + void SetEffectColor2( RageColor c ) { m_effectColor2 = c; } + void SetEffectPeriod( float fTime ); + float GetEffectPeriod() const; + void SetEffectTiming( float fRampUp, float fAtHalf, float fRampDown, float fAtZero ); + void SetEffectOffset( float fTime ) { m_fEffectOffset = fTime; } + void SetEffectClock( EffectClock c ) { m_EffectClock = c; } + void SetEffectClockString( const RString &s ); // convenience + + void SetEffectMagnitude( RageVector3 vec ) { m_vEffectMagnitude = vec; } + RageVector3 GetEffectMagnitude() const { return m_vEffectMagnitude; } + + void SetEffectDiffuseBlink( + float fEffectPeriodSeconds = 1.0f, + RageColor c1 = RageColor(0.5f,0.5f,0.5f,1), + RageColor c2 = RageColor(1,1,1,1) ); + void SetEffectDiffuseShift( float fEffectPeriodSeconds = 1.f, + RageColor c1 = RageColor(0,0,0,1), + RageColor c2 = RageColor(1,1,1,1) ); + void SetEffectDiffuseRamp( float fEffectPeriodSeconds = 1.f, + RageColor c1 = RageColor(0,0,0,1), + RageColor c2 = RageColor(1,1,1,1) ); + void SetEffectGlowBlink( float fEffectPeriodSeconds = 1.f, + RageColor c1 = RageColor(1,1,1,0.2f), + RageColor c2 = RageColor(1,1,1,0.8f) ); + void SetEffectGlowShift( + float fEffectPeriodSeconds = 1.0f, + RageColor c1 = RageColor(1,1,1,0.2f), + RageColor c2 = RageColor(1,1,1,0.8f) ); + void SetEffectGlowRamp( + float fEffectPeriodSeconds = 1.0f, + RageColor c1 = RageColor(1,1,1,0.2f), + RageColor c2 = RageColor(1,1,1,0.8f) ); + void SetEffectRainbow( + float fEffectPeriodSeconds = 2.0f ); + void SetEffectWag( + float fPeriod = 2.f, + RageVector3 vect = RageVector3(0,0,20) ); + void SetEffectBounce( + float fPeriod = 2.f, + RageVector3 vect = RageVector3(0,20,0) ); + void SetEffectBob( + float fPeriod = 2.f, + RageVector3 vect = RageVector3(0,20,0) ); + void SetEffectPulse( + float fPeriod = 2.f, + float fMinZoom = 0.5f, + float fMaxZoom = 1.f ); + void SetEffectSpin( RageVector3 vect = RageVector3(0,0,180) ); + void SetEffectVibrate( RageVector3 vect = RageVector3(10,10,10) ); + + + // other properties + /** + * @brief Determine if the Actor is visible at this time. + * @return true if it's visible, false otherwise. */ + bool GetVisible() const { return m_bVisible; } + void SetVisible( bool b ) { m_bVisible = b; } + void SetShadowLength( float fLength ) { m_fShadowLengthX = fLength; m_fShadowLengthY = fLength; } + void SetShadowLengthX( float fLengthX ) { m_fShadowLengthX = fLengthX; } + void SetShadowLengthY( float fLengthY ) { m_fShadowLengthY = fLengthY; } + void SetShadowColor( RageColor c ) { m_ShadowColor = c; } + // TODO: Implement hibernate as a tween type? + void SetHibernate( float fSecs ) { m_fHibernateSecondsLeft = fSecs; } + void SetDrawOrder( int iOrder ) { m_iDrawOrder = iOrder; } + int GetDrawOrder() const { return m_iDrawOrder; } + + virtual void EnableAnimation( bool b ) { m_bIsAnimating = b; } // Sprite needs to overload this + void StartAnimating() { this->EnableAnimation(true); } + void StopAnimating() { this->EnableAnimation(false); } + + // render states + void SetBlendMode( BlendMode mode ) { m_BlendMode = mode; } + void SetTextureWrapping( bool b ) { m_bTextureWrapping = b; } + void SetTextureFiltering( bool b ) { m_bTextureFiltering = b; } + void SetClearZBuffer( bool b ) { m_bClearZBuffer = b; } + void SetUseZBuffer( bool b ) { SetZTestMode(b?ZTEST_WRITE_ON_PASS:ZTEST_OFF); SetZWrite(b); } + virtual void SetZTestMode( ZTestMode mode ) { m_ZTestMode = mode; } + virtual void SetZWrite( bool b ) { m_bZWrite = b; } + void SetZBias( float f ) { m_fZBias = f; } + virtual void SetCullMode( CullMode mode ) { m_CullMode = mode; } + + // Lua + virtual void PushSelf( lua_State *L ); + virtual void PushContext( lua_State *L ); + + // Named commands + void AddCommand( const RString &sCmdName, apActorCommands apac ); + bool HasCommand( const RString &sCmdName ) const; + const apActorCommands *GetCommand( const RString &sCommandName ) const; + void PlayCommand( const RString &sCommandName ) { HandleMessage( Message(sCommandName) ); } // convenience + void PlayCommandNoRecurse( const Message &msg ); + + // Commands by reference + virtual void RunCommands( const LuaReference& cmds, const LuaReference *pParamTable = NULL ); + void RunCommands( const apActorCommands& cmds, const LuaReference *pParamTable = NULL ) { this->RunCommands( *cmds, pParamTable ); } // convenience + virtual void RunCommandsRecursively( const LuaReference& cmds, const LuaReference *pParamTable = NULL ) { RunCommands(cmds, pParamTable); } + // If we're a leaf, then execute this command. + virtual void RunCommandsOnLeaves( const LuaReference& cmds, const LuaReference *pParamTable = NULL ) { RunCommands(cmds, pParamTable); } + + // Messages + virtual void HandleMessage( const Message &msg ); + + // Animation + virtual int GetNumStates() const { return 1; } + virtual void SetState( int iNewState ) {} + virtual float GetAnimationLengthSeconds() const { return 0; } + virtual void SetSecondsIntoAnimation( float fSeconds ) {} + virtual void SetUpdateRate( float fRate ) {} + + HiddenPtr m_pLuaInstance; + +protected: + /** @brief the name of the Actor. */ + RString m_sName; + /** @brief the current parent of this Actor if it exists. */ + Actor *m_pParent; + + /** @brief Some general information about the Tween. */ + struct TweenInfo + { + // counters for tweening + TweenInfo(); + ~TweenInfo(); + TweenInfo( const TweenInfo &cpy ); + TweenInfo &operator=( const TweenInfo &rhs ); + + ITween *m_pTween; + /** @brief How far into the tween are we? */ + float m_fTimeLeftInTween; + /** @brief The number of seconds between Start and End positions/zooms. */ + float m_fTweenTime; + /** @brief The command to execute when this TweenState goes into effect. */ + RString m_sCommandName; + }; + + RageVector3 m_baseRotation; + RageVector3 m_baseScale; + float m_fBaseAlpha; + + RageVector2 m_size; + TweenState m_current; + TweenState m_start; + struct TweenStateAndInfo + { + TweenState state; + TweenInfo info; + }; + vector m_Tweens; + + /** @brief Temporary variables that are filled just before drawing */ + TweenState *m_pTempState; + + bool m_bFirstUpdate; + + // Stuff for alignment + /** @brief The particular horizontal alignment. + * + * Use the defined constant values for best effect. */ + float m_fHorizAlign; + /** @brief The particular vertical alignment. + * + * Use the defined constant values for best effect. */ + float m_fVertAlign; + + // Stuff for effects +#if defined(SSC_FUTURES) // be able to stack effects + vector m_Effects; +#else // compatibility + Effect m_Effect; +#endif + float m_fSecsIntoEffect; + float m_fEffectDelta; + + // units depend on m_EffectClock + float m_fEffectRampUp; + float m_fEffectHoldAtHalf; + float m_fEffectRampDown; + float m_fEffectHoldAtZero; + float m_fEffectOffset; + EffectClock m_EffectClock; + + /* This can be used in lieu of the fDeltaTime parameter to Update() to + * follow the effect clock. Actor::Update must be called first. */ + float GetEffectDeltaTime() const { return m_fEffectDelta; } + + // todo: account for SSC_FUTURES by having these be vectors too -aj + RageColor m_effectColor1; + RageColor m_effectColor2; + RageVector3 m_vEffectMagnitude; + + // other properties + bool m_bVisible; + bool m_bIsAnimating; + float m_fHibernateSecondsLeft; + float m_fShadowLengthX; + float m_fShadowLengthY; + RageColor m_ShadowColor; + /** @brief The draw order priority. + * + * The lower this number is, the sooner it is drawn. */ + int m_iDrawOrder; + + // render states + BlendMode m_BlendMode; + ZTestMode m_ZTestMode; + CullMode m_CullMode; + bool m_bTextureWrapping; + bool m_bTextureFiltering; + bool m_bClearZBuffer; + bool m_bZWrite; + /** + * @brief The amount of bias. + * + * If 0, there is no bias. If 1, there is a full bias. */ + float m_fZBias; + + // global state + static float g_fCurrentBGMTime, g_fCurrentBGMBeat; + static float g_fCurrentBGMTimeNoOffset, g_fCurrentBGMBeatNoOffset; + static vector g_vfCurrentBGMBeatPlayer; + static vector g_vfCurrentBGMBeatPlayerNoOffset; + +private: + // commands + map m_mapNameToCommands; +}; + +#endif + +/** + * @file + * @author Chris Danford (c) 2001-2004 + * @section LICENSE + * 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/src/Actors/ActorFrame.cpp b/src/Actors/ActorFrame.cpp new file mode 100644 index 0000000000..bb94b5b025 --- /dev/null +++ b/src/Actors/ActorFrame.cpp @@ -0,0 +1,633 @@ +#include "global.h" +#include "ActorFrame.h" +#include "arch/Dialog/Dialog.h" +#include "RageUtil.h" +#include "RageLog.h" +#include "XmlFile.h" +#include "ActorUtil.h" +#include "LuaBinding.h" +#include "ActorUtil.h" +#include "RageDisplay.h" +#include "ScreenDimensions.h" +#include "Foreach.h" + +/* Tricky: We need ActorFrames created in Lua to auto delete their children. + * We don't want classes that derive from ActorFrame to auto delete their + * children. The name "ActorFrame" is widely used in Lua, so we'll have + * that string instead create an ActorFrameAutoDeleteChildren object. + */ +//REGISTER_ACTOR_CLASS( ActorFrame ); +REGISTER_ACTOR_CLASS_WITH_NAME( ActorFrameAutoDeleteChildren, ActorFrame ); +ActorFrame *ActorFrame::Copy() const { return new ActorFrame(*this); } + + +ActorFrame::ActorFrame() +{ + m_bPropagateCommands = false; + m_bDeleteChildren = false; + m_bDrawByZPosition = false; + m_DrawFunction.SetFromNil(); + m_UpdateFunction.SetFromNil(); + m_fUpdateRate = 1; + m_fFOV = -1; + m_fVanishX = SCREEN_CENTER_X; + m_fVanishY = SCREEN_CENTER_Y; + m_bOverrideLighting = false; + m_bLighting = false; + m_ambientColor = RageColor(1,1,1,1); + m_diffuseColor = RageColor(1,1,1,1); + m_specularColor = RageColor(1,1,1,1); + m_lightDirection = RageVector3(0,0,1); +} + +ActorFrame::~ActorFrame() +{ + if( m_bDeleteChildren ) + DeleteAllChildren(); +} + +ActorFrame::ActorFrame( const ActorFrame &cpy ): + Actor( cpy ) +{ +#define CPY(x) this->x = cpy.x; + CPY( m_bPropagateCommands ); + CPY( m_bDeleteChildren ); + CPY( m_bDrawByZPosition ); + CPY( m_DrawFunction ); + CPY( m_UpdateFunction ); + CPY( m_fUpdateRate ); + CPY( m_fFOV ); + CPY( m_fVanishX ); + CPY( m_fVanishY ); + CPY( m_bOverrideLighting ); + CPY( m_bLighting ); + CPY( m_ambientColor ); + CPY( m_diffuseColor ); + CPY( m_specularColor ); + CPY( m_lightDirection ); +#undef CPY + + /* If m_bDeleteChildren, we own our children and it's up to us to copy + * them. If not, the derived class owns the children. This must preserve + * the current order of m_SubActors. */ + if( m_bDeleteChildren ) + { + for( unsigned i = 0; i < cpy.m_SubActors.size(); ++i ) + { + Actor *pActor = cpy.m_SubActors[i]->Copy(); + this->AddChild( pActor ); + } + } +} + +void ActorFrame::InitState() +{ + FOREACH( Actor*, m_SubActors, a ) + (*a)->InitState(); + Actor::InitState(); +} + +void ActorFrame::LoadFromNode( const XNode* pNode ) +{ + if( AutoLoadChildren() ) + LoadChildrenFromNode( pNode ); + + Actor::LoadFromNode( pNode ); + + pNode->GetAttrValue( "UpdateRate", m_fUpdateRate ); + pNode->GetAttrValue( "FOV", m_fFOV ); + pNode->GetAttrValue( "VanishX", m_fVanishX ); + pNode->GetAttrValue( "VanishY", m_fVanishY ); + m_bOverrideLighting = pNode->GetAttrValue( "Lighting", m_bLighting ); + // new lighting values (only ambient color seems to work?) -aj + RString sTemp1,sTemp2,sTemp3; + pNode->GetAttrValue( "AmbientColor", sTemp1 ); + m_ambientColor.FromString(sTemp1); + pNode->GetAttrValue( "DiffuseColor", sTemp2 ); + m_diffuseColor.FromString(sTemp2); + pNode->GetAttrValue( "SpecularColor", sTemp3 ); + m_specularColor.FromString(sTemp3); + // Values need to be converted into a RageVector3, so more work needs to be done... + //pNode->GetAttrValue( "LightDirection", m_lightDirection ); +} + +void ActorFrame::LoadChildrenFromNode( const XNode* pNode ) +{ + // Shouldn't be calling this unless we're going to delete our children. + ASSERT( m_bDeleteChildren ); + + // Load children + const XNode* pChildren = pNode->GetChild("children"); + bool bArrayOnly = false; + if( pChildren == NULL ) + { + bArrayOnly = true; + pChildren = pNode; + } + + FOREACH_CONST_Child( pChildren, pChild ) + { + if( bArrayOnly && !IsAnInt(pChild->GetName()) ) + continue; + + Actor* pChildActor = ActorUtil::LoadFromNode( pChild, this ); + if( pChildActor ) + AddChild( pChildActor ); + } + SortByDrawOrder(); +} + +void ActorFrame::AddChild( Actor *pActor ) +{ +#ifdef DEBUG + // check that this Actor isn't already added. + vector::iterator iter = find( m_SubActors.begin(), m_SubActors.end(), pActor ); + if( iter != m_SubActors.end() ) + Dialog::OK( ssprintf("Actor \"%s\" adds child \"%s\" more than once", GetLineage().c_str(), pActor->GetName().c_str()) ); +#endif + + ASSERT( pActor ); + ASSERT( (void*)pActor != (void*)0xC0000005 ); + m_SubActors.push_back( pActor ); + + pActor->SetParent( this ); +} + +void ActorFrame::RemoveChild( Actor *pActor ) +{ + vector::iterator iter = find( m_SubActors.begin(), m_SubActors.end(), pActor ); + if( iter != m_SubActors.end() ) + m_SubActors.erase( iter ); +} + +void ActorFrame::TransferChildren( ActorFrame *pTo ) +{ + FOREACH( Actor*, m_SubActors, i ) + pTo->AddChild( *i ); + RemoveAllChildren(); +} + +Actor* ActorFrame::GetChild( const RString &sName ) +{ + FOREACH( Actor*, m_SubActors, a ) + { + if( (*a)->GetName() == sName ) + return *a; + } + return NULL; +} + +void ActorFrame::RemoveAllChildren() +{ + m_SubActors.clear(); +} + +void ActorFrame::MoveToTail( Actor* pActor ) +{ + vector::iterator iter = find( m_SubActors.begin(), m_SubActors.end(), pActor ); + if( iter == m_SubActors.end() ) // didn't find + { + ASSERT(0); // called with a pActor that doesn't exist + return; + } + + m_SubActors.erase( iter ); + m_SubActors.push_back( pActor ); +} + +void ActorFrame::MoveToHead( Actor* pActor ) +{ + vector::iterator iter = find( m_SubActors.begin(), m_SubActors.end(), pActor ); + if( iter == m_SubActors.end() ) // didn't find + { + ASSERT(0); // called with a pActor that doesn't exist + return; + } + + m_SubActors.erase( iter ); + m_SubActors.insert( m_SubActors.begin(), pActor ); +} + +void ActorFrame::BeginDraw() +{ + Actor::BeginDraw(); + if( m_fFOV != -1 ) + { + DISPLAY->CameraPushMatrix(); + DISPLAY->LoadMenuPerspective( m_fFOV, SCREEN_WIDTH, SCREEN_HEIGHT, m_fVanishX, m_fVanishY ); + } + + if( m_bOverrideLighting ) + { + DISPLAY->SetLighting( m_bLighting ); + if( m_bLighting ) + DISPLAY->SetLightDirectional( 0,m_ambientColor,m_diffuseColor,m_specularColor,m_lightDirection ); + } +} + + +void ActorFrame::DrawPrimitives() +{ + ASSERT_M( !m_bClearZBuffer, "ClearZBuffer not supported on ActorFrames" ); + + // Don't set Actor-defined render states because we won't be drawing + // any geometry that belongs to this object. + // Actor::DrawPrimitives(); + + if( unlikely(!m_DrawFunction.IsNil()) ) + { + Lua *L = LUA->Get(); + m_DrawFunction.PushSelf( L ); + ASSERT( !lua_isnil(L, -1) ); + this->PushSelf( L ); + RString sError; + if( !LuaHelpers::RunScriptOnStack(L, sError, 1, 0) ) // 1 arg, 0 results + LOG->Warn( "Error running DrawFunction: %s", sError.c_str() ); + LUA->Release(L); + return; + } + + // draw all sub-ActorFrames while we're in the ActorFrame's local coordinate space + if( m_bDrawByZPosition ) + { + vector subs = m_SubActors; + ActorUtil::SortByZPosition( subs ); + for( unsigned i=0; iDraw(); + } + else + { + for( unsigned i=0; iDraw(); + } +} + + +void ActorFrame::EndDraw() +{ + if( m_bOverrideLighting ) + { + // TODO: pop state instead of turning lighting off + DISPLAY->SetLightOff( 0 ); + DISPLAY->SetLighting( false ); + } + + if( m_fFOV != -1 ) + { + DISPLAY->CameraPopMatrix(); + } + Actor::EndDraw(); +} + +void ActorFrame::PushChildrenTable( lua_State *L ) +{ + lua_newtable( L ); + FOREACH( Actor*, m_SubActors, a ) + { + LuaHelpers::Push( L, (*a)->GetName() ); + (*a)->PushSelf( L ); + lua_rawset( L, -3 ); + } +} + +void ActorFrame::PlayCommandOnChildren( const RString &sCommandName, const LuaReference *pParamTable ) +{ + const apActorCommands *pCmd = GetCommand( sCommandName ); + if( pCmd != NULL ) + RunCommandsOnChildren( *pCmd, pParamTable ); +} + +void ActorFrame::PlayCommandOnLeaves( const RString &sCommandName, const LuaReference *pParamTable ) +{ + const apActorCommands *pCmd = GetCommand( sCommandName ); + if( pCmd != NULL ) + RunCommandsOnLeaves( **pCmd, pParamTable ); +} + +void ActorFrame::RunCommandsRecursively( const LuaReference& cmds, const LuaReference *pParamTable ) +{ + for( unsigned i=0; iRunCommandsRecursively( cmds, pParamTable ); + Actor::RunCommandsRecursively( cmds, pParamTable ); +} + +void ActorFrame::RunCommandsOnChildren( const LuaReference& cmds, const LuaReference *pParamTable ) +{ + for( unsigned i=0; iRunCommands( cmds, pParamTable ); +} + +void ActorFrame::RunCommandsOnLeaves( const LuaReference& cmds, const LuaReference *pParamTable ) +{ + for( unsigned i=0; iRunCommandsOnLeaves( cmds, pParamTable ); +} + +void ActorFrame::UpdateInternal( float fDeltaTime ) +{ +// LOG->Trace( "ActorFrame::Update( %f )", fDeltaTime ); + + fDeltaTime *= m_fUpdateRate; + + Actor::UpdateInternal( fDeltaTime ); + + // update all sub-Actors + for( vector::iterator it=m_SubActors.begin(); it!=m_SubActors.end(); it++ ) + { + Actor *pActor = *it; + pActor->Update(fDeltaTime); + } + + if( unlikely(!m_UpdateFunction.IsNil()) ) + { + Lua *L = LUA->Get(); + m_UpdateFunction.PushSelf( L ); + ASSERT( !lua_isnil(L, -1) ); + this->PushSelf( L ); + lua_pushnumber( L, fDeltaTime ); + RString sError; + + if( !LuaHelpers::RunScriptOnStack(L, sError, 2, 0) ) // 1 args, 0 results + LOG->Warn( "Error running m_UpdateFunction: %s", sError.c_str() ); + LUA->Release(L); + } +} + +#define PropagateActorFrameCommand( cmd ) \ + void ActorFrame::cmd() \ + { \ + Actor::cmd(); \ + \ + /* set all sub-Actors */ \ + for( unsigned i=0; icmd(); \ + } + +#define PropagateActorFrameCommand1Param( cmd, type ) \ + void ActorFrame::cmd( type f ) \ + { \ + Actor::cmd( f ); \ + \ + /* set all sub-Actors */ \ + for( unsigned i=0; icmd( f ); \ + } + +PropagateActorFrameCommand( FinishTweening ) +PropagateActorFrameCommand1Param( SetDiffuse, RageColor ) +PropagateActorFrameCommand1Param( SetZTestMode, ZTestMode ) +PropagateActorFrameCommand1Param( SetZWrite, bool ) +PropagateActorFrameCommand1Param( HurryTweening, float ) +PropagateActorFrameCommand1Param( SetDiffuseAlpha, float ) +PropagateActorFrameCommand1Param( SetBaseAlpha, float ) + + +float ActorFrame::GetTweenTimeLeft() const +{ + float m = Actor::GetTweenTimeLeft(); + + for( unsigned i=0; iGetTweenTimeLeft()); + } + + return m; + +} + +static bool CompareActorsByDrawOrder(const Actor *p1, const Actor *p2) +{ + return p1->GetDrawOrder() < p2->GetDrawOrder(); +} + +void ActorFrame::SortByDrawOrder() +{ + // Preserve ordering of Actors with equal DrawOrders. + stable_sort( m_SubActors.begin(), m_SubActors.end(), CompareActorsByDrawOrder ); +} + +void ActorFrame::DeleteAllChildren() +{ + for( unsigned i=0; iHandleMessage( msg ); + } +} + +void ActorFrame::SetDrawByZPosition( bool b ) +{ + m_bDrawByZPosition = b; +} + + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ActorFrame. */ +class LunaActorFrame : public Luna +{ +public: + static int playcommandonchildren( T* p, lua_State *L ) { p->PlayCommandOnChildren(SArg(1)); return 0; } + static int playcommandonleaves( T* p, lua_State *L ) { p->PlayCommandOnLeaves(SArg(1)); return 0; } + static int runcommandsonleaves( T* p, lua_State *L ) + { + luaL_checktype( L, 1, LUA_TFUNCTION ); + LuaReference cmds; + cmds.SetFromStack( L ); + + p->RunCommandsOnLeaves( cmds ); + return 0; + } + static int RunCommandsOnChildren( T* p, lua_State *L ) + { + luaL_checktype( L, 1, LUA_TFUNCTION ); + lua_pushvalue( L, 2 ); + LuaReference ParamTable; + ParamTable.SetFromStack( L ); + + lua_pushvalue( L, 1 ); + LuaReference cmds; + cmds.SetFromStack( L ); + + p->RunCommandsOnChildren( cmds, &ParamTable ); + return 0; + } + static int propagate( T* p, lua_State *L ) { p->SetPropagateCommands( BIArg(1) ); return 0; } + static int fov( T* p, lua_State *L ) { p->SetFOV( FArg(1) ); return 0; } + static int SetUpdateRate( T* p, lua_State *L ) { p->SetUpdateRate( FArg(1) ); return 0; } + static int SetFOV( T* p, lua_State *L ) { p->SetFOV( FArg(1) ); return 0; } + static int vanishpoint( T* p, lua_State *L ) { p->SetVanishPoint( FArg(1), FArg(2) ); return 0; } + static int GetChild( T* p, lua_State *L ) + { + Actor *pChild = p->GetChild( SArg(1) ); + if( pChild ) + pChild->PushSelf( L ); + else + lua_pushnil( L ); + return 1; + } + static int GetChildren( T* p, lua_State *L ) + { + p->PushChildrenTable( L ); + return 1; + } + static int GetNumChildren( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumChildren() ); return 1; } + static int SetDrawByZPosition( T* p, lua_State *L ) { p->SetDrawByZPosition( BArg(1) ); return 1; } + static int SetDrawFunction( T* p, lua_State *L ) + { + luaL_checktype( L, 1, LUA_TFUNCTION ); + + LuaReference ref; + lua_pushvalue( L, 1 ); + ref.SetFromStack( L ); + p->SetDrawFunction( ref ); + return 0; + } + static int GetDrawFunction( T* p, lua_State *L ) + { + p->GetDrawFunction().PushSelf(L); + return 1; + } + static int SetUpdateFunction( T* p, lua_State *L ) + { + luaL_checktype( L, 1, LUA_TFUNCTION ); + + LuaReference ref; + lua_pushvalue( L, 1 ); + ref.SetFromStack( L ); + p->SetUpdateFunction( ref ); + return 0; + } + static int SortByDrawOrder( T* p, lua_State *L ) { p->SortByDrawOrder(); return 0; } + + //static int CustomLighting( T* p, lua_State *L ) { p->SetCustomLighting(BArg(1)); return 0; } + static int SetAmbientLightColor( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetAmbientLightColor( c ); return 0; } + static int SetDiffuseLightColor( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetDiffuseLightColor( c ); return 0; } + static int SetSpecularLightColor( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetSpecularLightColor( c ); return 0; } + static int SetLightDirection( T* p, lua_State *L ) + { + luaL_checktype( L, 1, LUA_TTABLE ); + lua_pushvalue( L, 1 ); + vector coords; + LuaHelpers::ReadArrayFromTable( coords, L ); + lua_pop( L, 1 ); + if( coords.size() !=3 ) + { + //error + } + RageVector3 vTmp = RageVector3( coords[0], coords[1], coords[2] ); + p->SetLightDirection( vTmp ); + return 0; + } + + static int AddChildFromPath( T* p, lua_State *L ) + { + // this one is tricky, we need to get an Actor from Lua. + Actor *pActor = ActorUtil::MakeActor( SArg(1) ); + if ( pActor == NULL ) + { + lua_pushboolean( L, 0 ); + return 1; + } + p->AddChild( pActor ); + lua_pushboolean( L, 1 ); + return 1; + } + + static int RemoveChild( T* p, lua_State *L ) + { + Actor *pChild = p->GetChild( SArg(1) ); + if( pChild ) + p->RemoveChild( pChild ); + else + lua_pushnil( L ); + return 1; + } + static int RemoveAllChildren( T* p, lua_State *L ) { p->RemoveAllChildren( ); return 0; } + + LunaActorFrame() + { + ADD_METHOD( playcommandonchildren ); + ADD_METHOD( playcommandonleaves ); + ADD_METHOD( runcommandsonleaves ); + ADD_METHOD( RunCommandsOnChildren ); + ADD_METHOD( propagate ); // deprecated + ADD_METHOD( fov ); + ADD_METHOD( SetUpdateRate ); + ADD_METHOD( SetFOV ); + ADD_METHOD( vanishpoint ); + ADD_METHOD( GetChild ); + ADD_METHOD( GetChildren ); + ADD_METHOD( GetNumChildren ); + ADD_METHOD( SetDrawByZPosition ); + ADD_METHOD( SetDrawFunction ); + ADD_METHOD( GetDrawFunction ); + ADD_METHOD( SetUpdateFunction ); + ADD_METHOD( SortByDrawOrder ); + //ADD_METHOD( CustomLighting ); + ADD_METHOD( SetAmbientLightColor ); + ADD_METHOD( SetDiffuseLightColor ); + ADD_METHOD( SetSpecularLightColor ); + ADD_METHOD( SetLightDirection ); + ADD_METHOD( AddChildFromPath ); + ADD_METHOD( RemoveChild ); + ADD_METHOD( RemoveAllChildren ); + + } +}; + +LUA_REGISTER_DERIVED_CLASS( ActorFrame, Actor ) +// lua end + +/* + * (c) 2001-2004 Chris Danford + * 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/src/Actors/ActorFrame.h b/src/Actors/ActorFrame.h new file mode 100644 index 0000000000..ba377ffcfb --- /dev/null +++ b/src/Actors/ActorFrame.h @@ -0,0 +1,164 @@ +#ifndef ACTORFRAME_H +#define ACTORFRAME_H + +#include "Actor.h" + +/** @brief A container for other Actors. */ +class ActorFrame : public Actor +{ +public: + ActorFrame(); + ActorFrame( const ActorFrame &cpy ); + virtual ~ActorFrame(); + + /** @brief Set up the initial state. */ + virtual void InitState(); + void LoadFromNode( const XNode* pNode ); + virtual ActorFrame *Copy() const; + + /** + * @brief Add a new child to the ActorFrame. + * @param pActor the new Actor to add. */ + virtual void AddChild( Actor *pActor ); + /** + * @brief Remove the specified child from the ActorFrame. + * @param pActor the Actor to remove. */ + virtual void RemoveChild( Actor *pActor ); + void TransferChildren( ActorFrame *pTo ); + Actor* GetChild( const RString &sName ); + vector GetChildren() { return m_SubActors; } + int GetNumChildren() const { return m_SubActors.size(); } + + /** @brief Remove all of the children from the frame. */ + void RemoveAllChildren(); + /** + * @brief Move a particular actor to the tail. + * @param pActor the actor to go to the tail. + */ + void MoveToTail( Actor* pActor ); + /** + * @brief Move a particular actor to the head. + * @param pActor the actor to go to the head. + */ + void MoveToHead( Actor* pActor ); + void SortByDrawOrder(); + void SetDrawByZPosition( bool b ); + + void SetDrawFunction( const LuaReference &DrawFunction ) { m_DrawFunction = DrawFunction; } + void SetUpdateFunction( const LuaReference &UpdateFunction ) { m_UpdateFunction = UpdateFunction; } + + LuaReference GetDrawFunction() const { return m_DrawFunction; } + virtual bool AutoLoadChildren() const { return false; } // derived classes override to automatically LoadChildrenFromNode + void DeleteChildrenWhenDone( bool bDelete=true ) { m_bDeleteChildren = bDelete; } + void DeleteAllChildren(); + + // Commands + virtual void PushSelf( lua_State *L ); + void PushChildrenTable( lua_State *L ); + void PlayCommandOnChildren( const RString &sCommandName, const LuaReference *pParamTable = NULL ); + void PlayCommandOnLeaves( const RString &sCommandName, const LuaReference *pParamTable = NULL ); + + virtual void RunCommandsRecursively( const LuaReference& cmds, const LuaReference *pParamTable = NULL ); + virtual void RunCommandsOnChildren( const LuaReference& cmds, const LuaReference *pParamTable = NULL ); /* but not on self */ + void RunCommandsOnChildren( const apActorCommands& cmds, const LuaReference *pParamTable = NULL ) { this->RunCommandsOnChildren( *cmds, pParamTable ); } // convenience + virtual void RunCommandsOnLeaves( const LuaReference& cmds, const LuaReference *pParamTable = NULL ); /* but not on self */ + + virtual void UpdateInternal( float fDeltaTime ); + virtual void BeginDraw(); + virtual void DrawPrimitives(); + virtual void EndDraw(); + + // propagated commands + virtual void SetDiffuse( RageColor c ); + virtual void SetDiffuseAlpha( float f ); + virtual void SetBaseAlpha( float f ); + virtual void SetZTestMode( ZTestMode mode ); + virtual void SetZWrite( bool b ); + virtual void FinishTweening(); + virtual void HurryTweening( float factor ); + + void SetUpdateRate( float fUpdateRate ) { m_fUpdateRate = fUpdateRate; } + void SetFOV( float fFOV ) { m_fFOV = fFOV; } + void SetVanishPoint( float fX, float fY) { m_fVanishX = fX; m_fVanishY = fY; } + + void SetCustomLighting( bool bCustomLighting ) { m_bOverrideLighting = bCustomLighting; } + void SetAmbientLightColor( RageColor c ) { m_ambientColor = c; } + void SetDiffuseLightColor( RageColor c ) { m_diffuseColor = c; } + void SetSpecularLightColor( RageColor c ) { m_specularColor = c; } + void SetLightDirection( RageVector3 vec ) { m_lightDirection = vec; } + + virtual void SetPropagateCommands( bool b ); + + /** @brief Amount of time until all tweens (and all children's tweens) have stopped: */ + virtual float GetTweenTimeLeft() const; + + virtual void HandleMessage( const Message &msg ); + virtual void RunCommands( const LuaReference& cmds, const LuaReference *pParamTable = NULL ); + void RunCommands( const apActorCommands& cmds, const LuaReference *pParamTable = NULL ) { this->RunCommands( *cmds, pParamTable ); } // convenience + +protected: + void LoadChildrenFromNode( const XNode* pNode ); + + /** @brief The children Actors used by the ActorFrame. */ + vector m_SubActors; + bool m_bPropagateCommands; + bool m_bDeleteChildren; + bool m_bDrawByZPosition; + LuaReference m_UpdateFunction; + LuaReference m_DrawFunction; + + // state effects + float m_fUpdateRate; + float m_fFOV; // -1 = no change + float m_fVanishX; + float m_fVanishY; + /** + * @brief A flad to see if an override for the lighting is needed. + * + * If true, set lightning to m_bLightning. */ + bool m_bOverrideLighting; + bool m_bLighting; + + // lighting variables + RageColor m_ambientColor; + RageColor m_diffuseColor; + RageColor m_specularColor; + RageVector3 m_lightDirection; +}; +/** @brief an ActorFrame that handles deleting children Actors automatically. */ +class ActorFrameAutoDeleteChildren : public ActorFrame +{ +public: + ActorFrameAutoDeleteChildren() { DeleteChildrenWhenDone(true); } + virtual bool AutoLoadChildren() const { return true; } + virtual ActorFrameAutoDeleteChildren *Copy() const; +}; + +#endif + +/** + * @file + * @author Chris Danford (c) 2001-2004 + * @section LICENSE + * 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/src/Actors/ActorFrameTexture.cpp b/src/Actors/ActorFrameTexture.cpp new file mode 100644 index 0000000000..4a907f7611 --- /dev/null +++ b/src/Actors/ActorFrameTexture.cpp @@ -0,0 +1,127 @@ +#include "global.h" +#include "ActorFrameTexture.h" +#include "RageTextureRenderTarget.h" +#include "RageTextureManager.h" +#include "ActorUtil.h" + +REGISTER_ACTOR_CLASS_WITH_NAME( ActorFrameTextureAutoDeleteChildren, ActorFrameTexture ); +ActorFrameTexture *ActorFrameTexture::Copy() const { return new ActorFrameTexture(*this); } + +ActorFrameTexture::ActorFrameTexture() +{ + m_bDepthBuffer = false; + m_bAlphaBuffer = false; + m_bFloat = false; + m_bPreserveTexture = false; + static uint64_t i = 0; + ++i; + m_sTextureName = ssprintf( ConvertI64FormatString("ActorFrameTexture %lli"), i ); + + m_pRenderTarget = NULL; +} + +ActorFrameTexture::ActorFrameTexture( const ActorFrameTexture &cpy ): + ActorFrame(cpy) +{ + FAIL_M( "ActorFrameTexture copy not implemented" ); +} + +ActorFrameTexture::~ActorFrameTexture() +{ + /* Release our reference to the texture. */ + TEXTUREMAN->UnloadTexture( m_pRenderTarget ); +} + +void ActorFrameTexture::Create() +{ + ASSERT( m_pRenderTarget == NULL ); + RageTextureID id( m_sTextureName ); + id.Policy = RageTextureID::TEX_VOLATILE; + + RenderTargetParam param; + param.bWithDepthBuffer = m_bDepthBuffer; + param.bWithAlpha = m_bAlphaBuffer; + param.bFloat = m_bFloat; + param.iWidth = (int) m_size.x; + param.iHeight = (int) m_size.y; + m_pRenderTarget = new RageTextureRenderTarget( id, param ); + m_pRenderTarget->m_bWasUsed = true; + + /* This passes ownership of m_pRenderTarget to TEXTUREMAN, but we retain + * our reference to it until we call TEXTUREMAN->UnloadTexture. */ + TEXTUREMAN->RegisterTexture( id, m_pRenderTarget ); +} + +void ActorFrameTexture::DrawPrimitives() +{ + if( m_pRenderTarget == NULL ) + return; + + m_pRenderTarget->BeginRenderingTo( m_bPreserveTexture ); + + ActorFrame::DrawPrimitives(); + + m_pRenderTarget->FinishRenderingTo(); +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ActorFrameTexture. */ +class LunaActorFrameTexture : public Luna +{ +public: + static int Create( T* p, lua_State *L ) { p->Create(); return 0; } + static int EnableDepthBuffer( T* p, lua_State *L ) { p->EnableDepthBuffer(BArg(1)); return 0; } + static int EnableAlphaBuffer( T* p, lua_State *L ) { p->EnableAlphaBuffer(BArg(1)); return 0; } + static int EnableFloat( T* p, lua_State *L ) { p->EnableFloat(BArg(1)); return 0; } + static int EnablePreserveTexture( T* p, lua_State *L ) { p->EnablePreserveTexture(BArg(1)); return 0; } + static int SetTextureName( T* p, lua_State *L ) { p->SetTextureName(SArg(1)); return 0; } + static int GetTexture( T* p, lua_State *L ) + { + RageTexture *pTexture = p->GetTexture(); + if( pTexture == NULL ) + return 0; + pTexture->PushSelf(L); + return 1; + } + + LunaActorFrameTexture() + { + ADD_METHOD( Create ); + ADD_METHOD( EnableDepthBuffer ); + ADD_METHOD( EnableAlphaBuffer ); + ADD_METHOD( EnableFloat ); + ADD_METHOD( EnablePreserveTexture ); + ADD_METHOD( SetTextureName ); + ADD_METHOD( GetTexture ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ActorFrameTexture, ActorFrame ) +// lua end + +/* + * (c) 2006 Glenn Maynard + * 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/src/Actors/ActorFrameTexture.h b/src/Actors/ActorFrameTexture.h new file mode 100644 index 0000000000..3997b7dcde --- /dev/null +++ b/src/Actors/ActorFrameTexture.h @@ -0,0 +1,88 @@ +#ifndef ACTOR_FRAME_TEXTURE_H +#define ACTOR_FRAME_TEXTURE_H + +#include "ActorFrame.h" +class RageTextureRenderTarget; + +class ActorFrameTexture: public ActorFrame +{ +public: + ActorFrameTexture(); + ActorFrameTexture( const ActorFrameTexture &cpy ); + virtual ~ActorFrameTexture(); + virtual ActorFrameTexture *Copy() const; + + /** + * @brief Set the texture name. + * + * This can be used with RageTextureManager (and users, eg. Sprite) + * to load the texture. If no name is supplied, a unique one will + * be generated. In that case, the only way to access the texture + * is via GetTextureName. + * @param sName the new name. */ + void SetTextureName( const RString &sName ) { m_sTextureName = sName; } + /** + * @brief Retrieve the texture name. + * @return the texture name. */ + RString GetTextureName() const { return m_sTextureName; } + RageTextureRenderTarget *GetTexture() { return m_pRenderTarget; } + + void EnableDepthBuffer( bool b ) { m_bDepthBuffer = b; } + void EnableAlphaBuffer( bool b ) { m_bAlphaBuffer = b; } + void EnableFloat( bool b ) { m_bFloat = b; } + void EnablePreserveTexture( bool b ) { m_bPreserveTexture = b; } + + void Create(); + + virtual void DrawPrimitives(); + + // Commands + virtual void PushSelf( lua_State *L ); + +private: + RageTextureRenderTarget *m_pRenderTarget; + + bool m_bDepthBuffer; + bool m_bAlphaBuffer; + bool m_bFloat; + bool m_bPreserveTexture; + /** @brief the name of this ActorFrameTexture. */ + RString m_sTextureName; +}; + +class ActorFrameTextureAutoDeleteChildren : public ActorFrameTexture +{ +public: + ActorFrameTextureAutoDeleteChildren() { DeleteChildrenWhenDone(true); } + virtual bool AutoLoadChildren() const { return true; } + virtual ActorFrameTextureAutoDeleteChildren *Copy() const; +}; + +#endif + +/** + * @file + * @author Glenn Maynard (c) 2006 + * @section LICENSE + * 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/src/Actors/ActorMultiTexture.cpp b/src/Actors/ActorMultiTexture.cpp new file mode 100644 index 0000000000..af2685f420 --- /dev/null +++ b/src/Actors/ActorMultiTexture.cpp @@ -0,0 +1,215 @@ +#include "global.h" +#include + +#include "ActorMultiTexture.h" +#include "RageTextureManager.h" +#include "XmlFile.h" +#include "RageLog.h" +#include "RageDisplay.h" +#include "RageTexture.h" +#include "RageUtil.h" +#include "ActorUtil.h" +#include "Foreach.h" +#include "LuaBinding.h" +#include "LuaManager.h" + +REGISTER_ACTOR_CLASS( ActorMultiTexture ); + + +ActorMultiTexture::ActorMultiTexture() +{ + m_EffectMode = EffectMode_Normal; +} + + +ActorMultiTexture::~ActorMultiTexture() +{ + ClearTextures(); +} + +ActorMultiTexture::ActorMultiTexture( const ActorMultiTexture &cpy ): + Actor( cpy ) +{ +#define CPY(a) a = cpy.a + CPY( m_Rect ); + CPY( m_aTextureUnits ); +#undef CPY + + FOREACH( TextureUnitState, m_aTextureUnits, tex ) + tex->m_pTexture = TEXTUREMAN->CopyTexture( tex->m_pTexture ); +} + +void ActorMultiTexture::SetTextureCoords( const RectF &r ) +{ + m_Rect = r; +} + +void ActorMultiTexture::LoadFromNode( const XNode* pNode ) +{ + m_Rect = RectF( 0, 0, 1, 1 ); + Actor::LoadFromNode( pNode ); +} + +void ActorMultiTexture::SetSizeFromTexture( RageTexture *pTexture ) +{ + ActorMultiTexture::m_size.x = pTexture->GetSourceWidth(); + ActorMultiTexture::m_size.y = pTexture->GetSourceHeight(); +} + +void ActorMultiTexture::ClearTextures() +{ + FOREACH( TextureUnitState, m_aTextureUnits, tex ) + TEXTUREMAN->UnloadTexture( tex->m_pTexture ); + m_aTextureUnits.clear(); +} + +int ActorMultiTexture::AddTexture( RageTexture *pTexture ) +{ + ASSERT( pTexture != NULL ); + LOG->Trace( "ActorMultiTexture::AddTexture( %s )", pTexture->GetID().filename.c_str() ); + + m_aTextureUnits.push_back( TextureUnitState() ); + m_aTextureUnits.back().m_pTexture = TEXTUREMAN->CopyTexture( pTexture ); + return m_aTextureUnits.size() - 1; +} + +void ActorMultiTexture::SetTextureMode( int iIndex, TextureMode tm ) +{ + ASSERT( iIndex < (int) m_aTextureUnits.size() ); + m_aTextureUnits[iIndex].m_TextureMode = tm; +} + +void ActorMultiTexture::DrawPrimitives() +{ + Actor::SetGlobalRenderStates(); // set Actor-specified render states + + RectF quadVerticies; + quadVerticies.left = -m_size.x/2.0f; + quadVerticies.right = +m_size.x/2.0f; + quadVerticies.top = -m_size.y/2.0f; + quadVerticies.bottom = +m_size.y/2.0f; + + DISPLAY->ClearAllTextures(); + for( size_t i = 0; i < m_aTextureUnits.size(); ++i ) + { + TextureUnit tu = enum_add2(TextureUnit_1, i); + DISPLAY->SetTexture( tu, m_aTextureUnits[i].m_pTexture->GetTexHandle() ); + DISPLAY->SetTextureWrapping( tu, m_bTextureWrapping ); + DISPLAY->SetTextureMode( tu, m_aTextureUnits[i].m_TextureMode ); + } + + DISPLAY->SetEffectMode( m_EffectMode ); + + static RageSpriteVertex v[4]; + v[0].p = RageVector3( quadVerticies.left, quadVerticies.top, 0 ); // top left + v[1].p = RageVector3( quadVerticies.left, quadVerticies.bottom, 0 ); // bottom left + v[2].p = RageVector3( quadVerticies.right, quadVerticies.bottom, 0 ); // bottom right + v[3].p = RageVector3( quadVerticies.right, quadVerticies.top, 0 ); // top right + + const RectF *pTexCoordRect = &m_Rect; + v[0].t = RageVector2( pTexCoordRect->left, pTexCoordRect->top ); // top left + v[1].t = RageVector2( pTexCoordRect->left, pTexCoordRect->bottom ); // bottom left + v[2].t = RageVector2( pTexCoordRect->right, pTexCoordRect->bottom ); // bottom right + v[3].t = RageVector2( pTexCoordRect->right, pTexCoordRect->top ); // top right + + v[0].c = m_pTempState->diffuse[0]; // top left + v[1].c = m_pTempState->diffuse[2]; // bottom left + v[2].c = m_pTempState->diffuse[3]; // bottom right + v[3].c = m_pTempState->diffuse[1]; // top right + + DISPLAY->DrawQuad( v ); + + for( size_t i = 0; i < m_aTextureUnits.size(); ++i ) + DISPLAY->SetTexture( enum_add2(TextureUnit_1, i), 0 ); + + DISPLAY->SetEffectMode( EffectMode_Normal ); +} + +bool ActorMultiTexture::EarlyAbortDraw() const +{ + return m_aTextureUnits.empty(); +} + + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ActorMultiTexture. */ +class LunaActorMultiTexture: public Luna +{ +public: + static int ClearTextures( T* p, lua_State *L ) + { + p->ClearTextures(); + return 0; + } + static int AddTexture( T* p, lua_State *L ) + { + RageTexture *pTexture = Luna::check(L, 1); + int iRet = p->AddTexture( pTexture ); + lua_pushinteger( L, iRet ); + return 1; + } + static int SetTextureMode( T* p, lua_State *L ) + { + int iIndex = IArg(1); + TextureMode tm = Enum::Check(L, 2); + p->SetTextureMode( iIndex, tm ); + return 0; + } + static int SetTextureCoords( T* p, lua_State *L ) + { + p->SetTextureCoords( RectF(FArg(1), FArg(2), FArg(3), FArg(4)) ); + return 0; + } + static int SetSizeFromTexture( T* p, lua_State *L ) + { + RageTexture *pTexture = Luna::check(L, 1); + p->SetSizeFromTexture( pTexture ); + return 0; + } + static int SetEffectMode( T* p, lua_State *L ) + { + EffectMode em = Enum::Check(L, 1); + p->SetEffectMode( em ); + return 0; + } + + LunaActorMultiTexture() + { + ADD_METHOD( ClearTextures ); + ADD_METHOD( AddTexture ); + ADD_METHOD( SetTextureMode ); + ADD_METHOD( SetTextureCoords ); + ADD_METHOD( SetSizeFromTexture ); + ADD_METHOD( SetEffectMode ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ActorMultiTexture, Actor ) +// lua end + +/* + * (c) 2001-2007 Glenn Maynard, Chris Danford + * 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/src/Actors/ActorMultiTexture.h b/src/Actors/ActorMultiTexture.h new file mode 100644 index 0000000000..6f4d793361 --- /dev/null +++ b/src/Actors/ActorMultiTexture.h @@ -0,0 +1,73 @@ +/** @brief ActorMultiTexture - A texture created from multiple textures. */ + +#ifndef ACTOR_MULTI_TEXTURE_H +#define ACTOR_MULTI_TEXTURE_H + +#include "Actor.h" +#include "RageDisplay.h" + +class RageTexture; + +class ActorMultiTexture: public Actor +{ +public: + ActorMultiTexture(); + ActorMultiTexture( const ActorMultiTexture &cpy ); + virtual ~ActorMultiTexture(); + + void LoadFromNode( const XNode* pNode ); + virtual ActorMultiTexture *Copy() const; + + virtual bool EarlyAbortDraw() const; + virtual void DrawPrimitives(); + + void ClearTextures(); + int AddTexture( RageTexture *pTexture ); + void SetTextureMode( int iIndex, TextureMode tm ); + + void SetSizeFromTexture( RageTexture *pTexture ); + void SetTextureCoords( const RectF &r ); + void SetEffectMode( EffectMode em ) { m_EffectMode = em; } + + virtual void PushSelf( lua_State *L ); + +private: + EffectMode m_EffectMode; + struct TextureUnitState + { + TextureUnitState(): m_pTexture(NULL), m_TextureMode(TextureMode_Modulate) {} + RageTexture *m_pTexture; + TextureMode m_TextureMode; + }; + vector m_aTextureUnits; + RectF m_Rect; +}; + +#endif + +/** + * @file + * @author Chris Danford (c) 2001-2004 + * @section LICENSE + * 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/src/Actors/ActorProxy.cpp b/src/Actors/ActorProxy.cpp new file mode 100644 index 0000000000..92f721f9b9 --- /dev/null +++ b/src/Actors/ActorProxy.cpp @@ -0,0 +1,90 @@ +#include "global.h" +#include "ActorProxy.h" +#include "ActorUtil.h" + +REGISTER_ACTOR_CLASS( ActorProxy ); + +ActorProxy::ActorProxy() +{ + m_pActorTarget = NULL; +} + +bool ActorProxy::EarlyAbortDraw() const +{ + return m_pActorTarget == NULL || Actor::EarlyAbortDraw(); +} + +void ActorProxy::DrawPrimitives() +{ + if( m_pActorTarget != NULL ) + { + bool bVisible = m_pActorTarget->GetVisible(); + m_pActorTarget->SetVisible( true ); + m_pActorTarget->Draw(); + m_pActorTarget->SetVisible( bVisible ); + } +} + +void ActorProxy::LoadFromNode( const XNode* pNode ) +{ + Actor::LoadFromNode( pNode ); +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ActorProxy. */ +class LunaActorProxy: public Luna +{ +public: + static int SetTarget( T* p, lua_State *L ) + { + Actor *pTarget = Luna::check( L, 1 ); + p->SetTarget( pTarget ); + return 0; + } + + static int GetTarget( T* p, lua_State *L ) + { + Actor *pTarget = p->GetTarget(); + if( pTarget != NULL ) + pTarget->PushSelf( L ); + else + lua_pushnil( L ); + return 1; + } + + LunaActorProxy() + { + ADD_METHOD( SetTarget ); + ADD_METHOD( GetTarget ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ActorProxy, Actor ) +// lua end + +/* + * (c) 2006 Glenn Maynard + * 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/src/Actors/ActorProxy.h b/src/Actors/ActorProxy.h new file mode 100644 index 0000000000..763ea74599 --- /dev/null +++ b/src/Actors/ActorProxy.h @@ -0,0 +1,54 @@ +#ifndef ACTOR_PROXY_H +#define ACTOR_PROXY_H + +#include "Actor.h" + +struct lua_State; +/** @brief Renders another actor. */ +class ActorProxy: public Actor +{ +public: + ActorProxy(); + + virtual bool EarlyAbortDraw() const; + virtual void DrawPrimitives(); + + void LoadFromNode( const XNode* pNode ); + virtual ActorProxy *Copy() const; + + Actor *GetTarget() { return m_pActorTarget; } + void SetTarget( Actor *pTarget ) { m_pActorTarget = pTarget; } + + // Lua + virtual void PushSelf( lua_State *L ); + +private: + Actor *m_pActorTarget; +}; + +#endif + +/* + * (c) 2006 Glenn Maynard + * 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/src/Actors/ActorScroller.cpp b/src/Actors/ActorScroller.cpp new file mode 100644 index 0000000000..e463af73b3 --- /dev/null +++ b/src/Actors/ActorScroller.cpp @@ -0,0 +1,397 @@ +#include "global.h" +#include "ActorScroller.h" +#include "Foreach.h" +#include "RageUtil.h" +#include "XmlFile.h" +#include "arch/Dialog/Dialog.h" +#include "RageLog.h" +#include "ActorUtil.h" +#include "LuaBinding.h" + +/* Tricky: We need ActorFrames created in Lua to auto delete their children. + * We don't want classes that derive from ActorFrame to auto delete their + * children. The name "ActorFrame" is widely used in Lua, so we'll have + * that string instead create an ActorFrameAutoDeleteChildren object. */ +//REGISTER_ACTOR_CLASS( ActorScroller ); +REGISTER_ACTOR_CLASS_WITH_NAME( ActorScrollerAutoDeleteChildren, ActorScroller ); +ActorScroller *ActorScroller::Copy() const { return new ActorScroller(*this); } + + +ActorScroller::ActorScroller() +{ + m_iNumItems = 0; + m_fCurrentItem = 0; + m_fDestinationItem = 0; + m_fSecondsPerItem = 1; + m_fSecondsPauseBetweenItems = 0; + m_fNumItemsToDraw = 7; + m_iFirstSubActorIndex = 0; + m_bLoop = false; + m_bFastCatchup = false; + m_bFunctionDependsOnPositionOffset = true; + m_bFunctionDependsOnItemIndex = true; + m_fPauseCountdownSeconds = 0; + m_fQuantizePixels = 0; + + m_quadMask.SetBlendMode( BLEND_NO_EFFECT ); // don't change color values + m_quadMask.SetUseZBuffer( true ); // we want to write to the Zbuffer + m_fMaskWidth = 0; + m_fMaskHeight = 0; + DisableMask(); +} + +void ActorScroller::Load2() +{ + m_iNumItems = m_SubActors.size(); + + Lua *L = LUA->Get(); + for( unsigned i = 0; i < m_SubActors.size(); ++i ) + { + lua_pushnumber( L, i ); + this->m_SubActors[i]->m_pLuaInstance->Set( L, "ItemIndex" ); + } + LUA->Release( L ); +} + +void ActorScroller::SetTransformFromReference( const LuaReference &ref ) +{ + m_exprTransformFunction.SetFromReference( ref ); + + // Probe to find which of the parameters are used. +#define GP(a,b) m_exprTransformFunction.GetTransformCached( a, b, 2 ) + m_bFunctionDependsOnPositionOffset = (GP(0,0) != GP(1,0)) && (GP(0,1) != GP(1,1)); + m_bFunctionDependsOnItemIndex = (GP(0,0) != GP(0,1)) && (GP(1,0) != GP(1,1)); + m_exprTransformFunction.ClearCache(); +} + +void ActorScroller::SetTransformFromExpression( const RString &sTransformFunction ) +{ + LuaReference ref; + ref.SetFromExpression( sTransformFunction ); + SetTransformFromReference( ref ); +} + +void ActorScroller::SetTransformFromWidth( float fItemWidth ) +{ + SetTransformFromExpression( ssprintf("function(self,offset,itemIndex,numItems) self:x(%f*offset) end",fItemWidth) ); +} + +void ActorScroller::SetTransformFromHeight( float fItemHeight ) +{ + SetTransformFromExpression( ssprintf("function(self,offset,itemIndex,numItems) self:y(%f*offset) end",fItemHeight) ); +} + +void ActorScroller::EnableMask( float fWidth, float fHeight ) +{ + m_quadMask.SetVisible( fWidth != 0 && fHeight != 0 ); + m_quadMask.SetWidth( fWidth ); + m_fMaskWidth = fWidth; + m_quadMask.SetHeight( fHeight ); + m_fMaskHeight = fHeight; +} + +void ActorScroller::DisableMask() +{ + m_quadMask.SetVisible( false ); +} + +void ActorScroller::ScrollThroughAllItems() +{ + m_fCurrentItem = m_bLoop ? +m_fNumItemsToDraw/2.0f : -(m_fNumItemsToDraw/2.0f)-1; + m_fDestinationItem = (float)(m_iNumItems+m_fNumItemsToDraw/2.0f+1); +} + +void ActorScroller::ScrollWithPadding( float fItemPaddingStart, float fItemPaddingEnd ) +{ + m_fCurrentItem = -fItemPaddingStart; + m_fDestinationItem = m_iNumItems-1+fItemPaddingEnd; +} + +float ActorScroller::GetSecondsForCompleteScrollThrough() const +{ + float fTotalItems = m_fNumItemsToDraw + m_iNumItems; + return fTotalItems * (m_fSecondsPerItem + m_fSecondsPauseBetweenItems ); +} + +float ActorScroller::GetSecondsToDestination() const +{ + float fTotalItemsToMove = fabsf(m_fCurrentItem - m_fDestinationItem); + return fTotalItemsToMove * m_fSecondsPerItem; +} + +void ActorScroller::LoadFromNode( const XNode *pNode ) +{ + ActorFrame::LoadFromNode( pNode ); + + Load2(); + + float fNumItemsToDraw = 0; + if( pNode->GetAttrValue("NumItemsToDraw", fNumItemsToDraw) ) + SetNumItemsToDraw( fNumItemsToDraw ); + + float fSecondsPerItem = 0; + if( pNode->GetAttrValue("SecondsPerItem", fSecondsPerItem) ) + ActorScroller::SetSecondsPerItem( fSecondsPerItem ); + + Lua *L = LUA->Get(); + pNode->PushAttrValue( L, "TransformFunction" ); + { + LuaReference ref; + ref.SetFromStack( L ); + if( !ref.IsNil() ) + SetTransformFromReference( ref ); + } + LUA->Release( L ); + + int iSubdivisions = 1; + if( pNode->GetAttrValue("Subdivisions", iSubdivisions) ) + ActorScroller::SetNumSubdivisions( iSubdivisions ); + + bool bUseMask = false; + pNode->GetAttrValue( "UseMask", bUseMask ); + + if( bUseMask ) + { + pNode->GetAttrValue( "MaskWidth", m_fMaskWidth ); + pNode->GetAttrValue( "MaskHeight", m_fMaskHeight ); + EnableMask( m_fMaskWidth, m_fMaskHeight ); + } + + pNode->GetAttrValue( "QuantizePixels", m_fQuantizePixels ); +} + +void ActorScroller::UpdateInternal( float fDeltaTime ) +{ + ActorFrame::UpdateInternal( fDeltaTime ); + + // If we have no children, the code below will busy loop. + if( !m_SubActors.size() ) + return; + + // handle pause + if( fDeltaTime > m_fPauseCountdownSeconds ) + { + fDeltaTime -= m_fPauseCountdownSeconds; + m_fPauseCountdownSeconds = 0; + } + else + { + m_fPauseCountdownSeconds -= fDeltaTime; + fDeltaTime = 0; + return; + } + + + if( m_fCurrentItem == m_fDestinationItem ) + return; // done scrolling + + + float fOldItemAtTop = m_fCurrentItem; + if( m_fSecondsPerItem > 0 ) + { + float fApproachSpeed = fDeltaTime/m_fSecondsPerItem; + if( m_bFastCatchup ) + { + float fDistanceToMove = fabsf(m_fCurrentItem - m_fDestinationItem); + if( fDistanceToMove > 1 ) + fApproachSpeed *= fDistanceToMove*fDistanceToMove; + } + + fapproach( m_fCurrentItem, m_fDestinationItem, fApproachSpeed ); + } + + // if items changed, then pause + if( (int)fOldItemAtTop != (int)m_fCurrentItem ) + m_fPauseCountdownSeconds = m_fSecondsPauseBetweenItems; + + if( m_bLoop ) + m_fCurrentItem = fmodf( m_fCurrentItem, (float) m_iNumItems ); +} + +void ActorScroller::DrawPrimitives() +{ + PositionItemsAndDrawPrimitives( true ); +} + +void ActorScroller::PositionItems() +{ + PositionItemsAndDrawPrimitives( false ); +} + +/* Shift m_SubActors forward by iDist. This will place item m_iFirstSubActorIndex + * in m_SubActors[0]. */ +void ActorScroller::ShiftSubActors( int iDist ) +{ + if( iDist != INT_MAX ) + CircularShift( m_SubActors, iDist ); +} + +void ActorScroller::PositionItemsAndDrawPrimitives( bool bDrawPrimitives ) +{ + if( m_SubActors.empty() ) + return; + + float fNumItemsToDraw = m_fNumItemsToDraw; + if( m_quadMask.GetVisible() ) + { + // write to z buffer so that top and bottom are clipped + // Draw an extra item; this is the one that will be masked. + fNumItemsToDraw++; + float fPositionFullyOffScreenTop = -(fNumItemsToDraw)/2.f; + float fPositionFullyOffScreenBottom = (fNumItemsToDraw)/2.f; + + m_exprTransformFunction.TransformItemCached( m_quadMask, fPositionFullyOffScreenTop, -1, m_iNumItems ); + if( bDrawPrimitives ) m_quadMask.Draw(); + + m_exprTransformFunction.TransformItemCached( m_quadMask, fPositionFullyOffScreenBottom, m_iNumItems, m_iNumItems ); + if( bDrawPrimitives ) m_quadMask.Draw(); + } + + float fFirstItemToDraw = m_fCurrentItem - fNumItemsToDraw/2.f; + float fLastItemToDraw = m_fCurrentItem + fNumItemsToDraw/2.f; + int iFirstItemToDraw = (int) ceilf( fFirstItemToDraw ); + int iLastItemToDraw = (int) ceilf( fLastItemToDraw ); + if( !m_bLoop ) + { + iFirstItemToDraw = clamp( iFirstItemToDraw, 0, m_iNumItems ); + iLastItemToDraw = clamp( iLastItemToDraw, 0, m_iNumItems ); + } + + bool bDelayedDraw = m_bDrawByZPosition && !m_bLoop; + vector subs; + + { + // Shift m_SubActors so iFirstItemToDraw is at the beginning. + int iNewFirstIndex = iFirstItemToDraw; + int iDist = iNewFirstIndex - m_iFirstSubActorIndex; + m_iFirstSubActorIndex = iNewFirstIndex; + ShiftSubActors( iDist ); + } + + int iNumToDraw = iLastItemToDraw - iFirstItemToDraw; + for( int i = 0; i < iNumToDraw; ++i ) + { + int iItem = i + iFirstItemToDraw; + float fPosition = iItem - m_fCurrentItem; + int iIndex = i; // index into m_SubActors + if( m_bLoop ) + wrap( iIndex, m_SubActors.size() ); + else if( iIndex < 0 || iIndex >= (int)m_SubActors.size() ) + continue; + + // Optimization: Zero out unused parameters so that they don't create new, + // unnecessary entries in the position cache. On scrollers with lots of + // items, especially with Subdivisions > 1, m_exprTransformFunction uses + // too much memory. + if( !m_bFunctionDependsOnPositionOffset ) + fPosition = 0; + if( !m_bFunctionDependsOnItemIndex ) + iItem = 0; + + m_exprTransformFunction.TransformItemCached( *m_SubActors[iIndex], fPosition, iItem, m_iNumItems ); + if( bDrawPrimitives ) + { + if( bDelayedDraw ) + subs.push_back( m_SubActors[iIndex] ); + else + m_SubActors[iIndex]->Draw(); + } + } + + if( bDelayedDraw ) + { + ActorUtil::SortByZPosition( subs ); + FOREACH( Actor*, subs, a ) + (*a)->Draw(); + } +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ActorScroller. */ +class LunaActorScroller: public Luna +{ +public: + static int PositionItems( T* p, lua_State *L ) { p->PositionItems(); return 0; } + static int SetTransformFromFunction( T* p, lua_State *L ) + { + LuaReference ref; + LuaHelpers::FromStack( L, ref, 1 ); + p->SetTransformFromReference( ref ); + return 0; + } + static int SetTransformFromHeight( T* p, lua_State *L ) { p->SetTransformFromHeight(FArg(1)); return 0; } + static int SetTransformFromWidth( T* p, lua_State *L ) { p->SetTransformFromWidth(FArg(1)); return 0; } + static int SetCurrentAndDestinationItem( T* p, lua_State *L ) { p->SetCurrentAndDestinationItem( FArg(1) ); return 0; } + static int SetDestinationItem( T* p, lua_State *L ) { p->SetDestinationItem( FArg(1) ); return 0; } + static int GetSecondsToDestination( T* p, lua_State *L ) { lua_pushnumber( L, p->GetSecondsToDestination() ); return 1; } + static int SetSecondsPerItem( T* p, lua_State *L ) { p->SetSecondsPerItem(FArg(1)); return 0; } + static int SetSecondsPauseBetweenItems( T* p, lua_State *L ) { p->SetSecondsPauseBetweenItems(FArg(1)); return 0; } + static int SetPauseCountdownSeconds( T* p, lua_State *L ) { p->SetPauseCountdownSeconds(FArg(1)); return 0; } + static int SetNumSubdivisions( T* p, lua_State *L ) { p->SetNumSubdivisions(IArg(1)); return 0; } + static int ScrollThroughAllItems( T* p, lua_State *L ) { p->ScrollThroughAllItems(); return 0; } + static int ScrollWithPadding( T* p, lua_State *L ) { p->ScrollWithPadding(FArg(1),FArg(2)); return 0; } + static int SetFastCatchup( T* p, lua_State *L ) { p->SetFastCatchup(BArg(1)); return 0; } + static int SetLoop( T* p, lua_State *L ) { p->SetLoop(BArg(1)); return 0; } + static int SetMask( T* p, lua_State *L ) { p->EnableMask(FArg(1), FArg(2)); return 0; } + + static int SetNumItemsToDraw( T* p, lua_State *L ) { p->SetNumItemsToDraw(FArg(1)); return 0; } + static int GetFullScrollLengthSeconds( T* p, lua_State *L ) { lua_pushnumber( L, p->GetSecondsForCompleteScrollThrough() ); return 1; } + static int GetCurrentItem( T* p, lua_State *L ) { lua_pushnumber( L, p->GetCurrentItem() ); return 1; } + static int GetDestinationItem( T* p, lua_State *L ) { lua_pushnumber( L, p->GetDestinationItem() ); return 1; } + static int GetNumItems( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumItems() ); return 1; } + + LunaActorScroller() + { + ADD_METHOD( PositionItems ); + ADD_METHOD( SetTransformFromFunction ); + ADD_METHOD( SetTransformFromHeight ); + ADD_METHOD( SetTransformFromWidth ); + ADD_METHOD( SetCurrentAndDestinationItem ); + ADD_METHOD( SetDestinationItem ); + ADD_METHOD( GetSecondsToDestination ); + ADD_METHOD( SetSecondsPerItem ); + ADD_METHOD( SetSecondsPauseBetweenItems ); + ADD_METHOD( SetPauseCountdownSeconds ); + ADD_METHOD( SetNumSubdivisions ); + ADD_METHOD( ScrollThroughAllItems ); + ADD_METHOD( ScrollWithPadding ); + ADD_METHOD( SetFastCatchup ); + ADD_METHOD( SetLoop ); + ADD_METHOD( SetMask ); + ADD_METHOD( SetNumItemsToDraw ); + ADD_METHOD( GetFullScrollLengthSeconds ); + ADD_METHOD( GetCurrentItem ); + ADD_METHOD( GetDestinationItem ); + ADD_METHOD( GetNumItems ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ActorScroller, ActorFrame ) +// lua end + +/* + * (c) 2003-2004 Chris Danford + * 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/src/Actors/ActorScroller.h b/src/Actors/ActorScroller.h new file mode 100644 index 0000000000..2a195dcde2 --- /dev/null +++ b/src/Actors/ActorScroller.h @@ -0,0 +1,123 @@ +#ifndef ActorScroller_H +#define ActorScroller_H + +#include "ActorFrame.h" +#include "Quad.h" +class XNode; +#include "LuaExpressionTransform.h" +/** @brief ActorFrame that moves its children. */ +class ActorScroller : public ActorFrame +{ +public: + ActorScroller(); + + void SetTransformFromReference( const LuaReference &ref ); + void SetTransformFromExpression( const RString &sTransformFunction ); + void SetTransformFromWidth( float fItemWidth ); + void SetTransformFromHeight( float fItemHeight ); + + void Load2(); + + void EnableMask( float fWidth, float fHeight ); + void DisableMask(); + + virtual void UpdateInternal( float fDelta ); + virtual void DrawPrimitives(); // handles drawing and doesn't call ActorFrame::DrawPrimitives + + void PositionItems(); + + void LoadFromNode( const XNode *pNode ); + virtual ActorScroller *Copy() const; + + void SetLoop( bool bLoop ) { m_bLoop = bLoop; } + void SetNumItemsToDraw( float fNumItemsToDraw ) { m_fNumItemsToDraw = fNumItemsToDraw; } + void SetDestinationItem( float fItemIndex ) { m_fDestinationItem = fItemIndex; } + void SetCurrentAndDestinationItem( float fItemIndex ) { m_fCurrentItem = m_fDestinationItem = fItemIndex; } + float GetCurrentItem() const { return m_fCurrentItem; } + float GetDestinationItem() const { return m_fDestinationItem; } + void ScrollThroughAllItems(); + void ScrollWithPadding( float fItemPaddingStart, float fItemPaddingEnd ); + void SetPauseCountdownSeconds( float fSecs ) { m_fPauseCountdownSeconds = fSecs; } + void SetFastCatchup( bool bOn ) { m_bFastCatchup = bOn; } + void SetSecondsPerItem( float fSeconds ) { m_fSecondsPerItem = fSeconds; } + void SetSecondsPauseBetweenItems( float fSeconds ) { m_fSecondsPauseBetweenItems = fSeconds; } + void SetNumSubdivisions( int iNumSubdivisions ) { m_exprTransformFunction.SetNumSubdivisions( iNumSubdivisions ); } + float GetSecondsForCompleteScrollThrough() const; + float GetSecondsToDestination() const; + int GetNumItems() const { return m_iNumItems; } + + // Commands + void PushSelf( lua_State *L ); + +protected: + void PositionItemsAndDrawPrimitives( bool bDrawPrimitives ); + virtual void ShiftSubActors( int iDist ); + + int m_iNumItems; + /** + * @brief the current item we are focused on. + * + * An item at the center of the list, usually between 0 and m_SubActors.size(), + * will approach its destination. + * + * The above comment was paraphrased from what was here previously. It could use + * some clearing up. -Wolfman2000 */ + float m_fCurrentItem; + float m_fDestinationItem; + /** + * @brief How many seconds are there per item? + * + * If this is less than zero, then we are not scrolling. */ + float m_fSecondsPerItem; + float m_fSecondsPauseBetweenItems; + float m_fNumItemsToDraw; + int m_iFirstSubActorIndex; + bool m_bLoop; + bool m_bFastCatchup; + bool m_bFunctionDependsOnPositionOffset; + bool m_bFunctionDependsOnItemIndex; + float m_fPauseCountdownSeconds; + float m_fQuantizePixels; + + Quad m_quadMask; + float m_fMaskWidth, m_fMaskHeight; + + LuaExpressionTransform m_exprTransformFunction; // params: self,offset,itemIndex,numItems +}; + +class ActorScrollerAutoDeleteChildren : public ActorScroller +{ +public: + ActorScrollerAutoDeleteChildren() { DeleteChildrenWhenDone(true); } + virtual bool AutoLoadChildren() const { return true; } + virtual ActorScrollerAutoDeleteChildren *Copy() const; +}; + +#endif + +/** + * @file + * @author Chris Danford (c) 2003-2004 + * @section LICENSE + * 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/src/Actors/ActorSound.cpp b/src/Actors/ActorSound.cpp new file mode 100644 index 0000000000..588a6635af --- /dev/null +++ b/src/Actors/ActorSound.cpp @@ -0,0 +1,103 @@ +#include "global.h" +#include "ActorSound.h" +#include "ActorUtil.h" +#include "LuaManager.h" +#include "XmlFile.h" +#include "RageUtil.h" + +REGISTER_ACTOR_CLASS_WITH_NAME( ActorSound, Sound ); + +void ActorSound::Load( const RString &sPath ) +{ + m_Sound.Load( sPath, true ); +} + +void ActorSound::Play() +{ + // This fix makes it possible to stop and pause ActorSounds. (Also, + // sometimes stacking sounds is annoying.) -DaisuMaster + if( m_Sound.IsPlaying() ) + { + m_Sound.PlayCopy(); + return; + } + m_Sound.StartPlaying(); +} + +void ActorSound::Pause( bool bPause ) +{ + m_Sound.Pause(bPause); +} + +void ActorSound::Stop() +{ + m_Sound.Stop(); +} + +void ActorSound::LoadFromNode( const XNode* pNode ) +{ + RageSoundLoadParams params; + pNode->GetAttrValue("SupportPan", params.m_bSupportPan); + pNode->GetAttrValue("SupportRateChanging", params.m_bSupportRateChanging); + + bool bPrecache = true; + pNode->GetAttrValue( "Precache", bPrecache ); + + Actor::LoadFromNode( pNode ); + + RString sFile; + if( ActorUtil::GetAttrPath(pNode, "File", sFile) ) + m_Sound.Load( sFile, bPrecache, ¶ms ); +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to the ActorSound. */ +class LunaActorSound: public Luna +{ +public: + static int load( T* p, lua_State *L ) { p->Load(SArg(1)); return 0; } + static int play( T* p, lua_State *L ) { p->Play(); return 0; } + static int pause( T* p, lua_State *L ) { p->Pause(BArg(1)); return 0; } + static int stop( T* p, lua_State *L ) { p->Stop(); return 0; } + static int get( T* p, lua_State *L ) { p->PushSound( L ); return 1; } + + LunaActorSound() + { + ADD_METHOD( load ); + ADD_METHOD( play ); + ADD_METHOD( pause ); + ADD_METHOD( stop ); + ADD_METHOD( get ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ActorSound, Actor ) +// lua end + + +/* + * (c) 2005 Glenn Maynard + * 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/src/Actors/ActorSound.h b/src/Actors/ActorSound.h new file mode 100644 index 0000000000..8b1d29d90a --- /dev/null +++ b/src/Actors/ActorSound.h @@ -0,0 +1,56 @@ +#ifndef ACTOR_SOUND_H +#define ACTOR_SOUND_H + +#include "Actor.h" +#include "RageSound.h" +/** @brief RageSound Actor interface. */ +class ActorSound: public Actor +{ +public: + virtual ~ActorSound() { } + virtual ActorSound *Copy() const; + + void Load( const RString &sPath ); + void Play(); + void Pause( bool bPause ); + void Stop(); + void LoadFromNode( const XNode* pNode ); + void PushSound( lua_State *L ) { m_Sound.PushSelf( L ); } + + // + // Lua + // + virtual void PushSelf( lua_State *L ); + +private: + RageSound m_Sound; +}; + +#endif + +/** + * @file + * @author Glenn Maynard (c) 2005 + * @section LICENSE + * 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. + */