diff --git a/src/Actor.cpp b/src/Actor.cpp index 402899c411..318e06e8ab 100644 --- a/src/Actor.cpp +++ b/src/Actor.cpp @@ -164,6 +164,7 @@ Actor::Actor() m_size = RageVector2( 1, 1 ); InitState(); m_pParent = NULL; + m_FakeParent= NULL; m_bFirstUpdate = true; } @@ -183,6 +184,7 @@ Actor::Actor( const Actor &cpy ): #define CPY(x) x = cpy.x CPY( m_sName ); CPY( m_pParent ); + CPY( m_FakeParent ); CPY( m_pLuaInstance ); CPY( m_baseRotation ); @@ -279,24 +281,56 @@ void Actor::LoadFromNode( const XNode* pNode ) PlayCommandNoRecurse( Message("Init") ); } +bool Actor::PartiallyOpaque() +{ + return m_pTempState->diffuse[0].a > 0 || m_pTempState->diffuse[1].a > 0 || + m_pTempState->diffuse[2].a > 0 || m_pTempState->diffuse[3].a > 0 || + m_pTempState->glow.a > 0; +} + void Actor::Draw() { if( !m_bVisible || m_fHibernateSecondsLeft > 0 || this->EarlyAbortDraw() ) + { return; // early abort + } + bool fake_parent_partially_opaque= true; + if(m_FakeParent) + { + if(!m_FakeParent->m_bVisible || m_FakeParent->m_fHibernateSecondsLeft > 0 + || m_FakeParent->EarlyAbortDraw()) + { + return; + } + m_FakeParent->PreDraw(); + fake_parent_partially_opaque= m_FakeParent->PartiallyOpaque(); + } this->PreDraw(); ASSERT( m_pTempState != NULL ); - if( m_pTempState->diffuse[0].a > 0 || m_pTempState->diffuse[1].a > 0 || m_pTempState->diffuse[2].a > 0 || m_pTempState->diffuse[3].a > 0 || m_pTempState->glow.a > 0 ) // This Actor is not fully transparent - { + if(PartiallyOpaque() && fake_parent_partially_opaque) + { + if(m_FakeParent) + { + m_FakeParent->BeginDraw(); + } // call the most-derived versions this->BeginDraw(); this->DrawPrimitives(); // call the most-derived version of DrawPrimitives(); this->EndDraw(); + if(m_FakeParent) + { + m_FakeParent->EndDraw(); + } } this->PostDraw(); + if(m_FakeParent) + { + m_FakeParent->PostDraw(); + } m_pTempState = NULL; } diff --git a/src/Actor.h b/src/Actor.h index 2be5f711bd..781533987d 100644 --- a/src/Actor.h +++ b/src/Actor.h @@ -232,6 +232,8 @@ public: float aux; }; + // PartiallyOpaque broken out of Draw for reuse and clarity. + bool PartiallyOpaque(); /** * @brief Calls multiple functions for drawing the Actors. * @@ -305,6 +307,9 @@ public: * @return the Actor's lineage. */ RString GetLineage() const; + void SetFakeParent(Actor* mailman) { m_FakeParent= mailman; } + Actor* GetFakeParent() { return m_FakeParent; } + /** * @brief Retrieve the Actor's x position. * @return the Actor's x position. */ @@ -607,6 +612,10 @@ protected: RString m_sName; /** @brief the current parent of this Actor if it exists. */ Actor *m_pParent; + // m_FakeParent exists to provide a way to render the actor inside another's + // state without making that actor the parent. It's like having multiple + // parents. -Kyz + Actor* m_FakeParent; /** @brief Some general information about the Tween. */ struct TweenInfo diff --git a/src/GhostArrowRow.cpp b/src/GhostArrowRow.cpp index fef07474e7..2afe66e1e0 100644 --- a/src/GhostArrowRow.cpp +++ b/src/GhostArrowRow.cpp @@ -35,6 +35,15 @@ void GhostArrowRow::Load( const PlayerState* pPlayerState, float fYReverseOffset } } +void GhostArrowRow::SetColumnRenderers(vector& renderers) +{ + ASSERT_M(renderers.size() == m_Ghost.size(), "Notefield has different number of columns than ghost row."); + for(size_t c= 0; c < m_Ghost.size(); ++c) + { + m_Ghost[c]->SetFakeParent(&(renderers[c])); + } +} + GhostArrowRow::~GhostArrowRow() { for( unsigned i = 0; i < m_Ghost.size(); ++i ) diff --git a/src/GhostArrowRow.h b/src/GhostArrowRow.h index b1647d3aa9..c091b04a81 100644 --- a/src/GhostArrowRow.h +++ b/src/GhostArrowRow.h @@ -4,6 +4,7 @@ #include "ActorFrame.h" #include "GameConstantsAndTypes.h" #include "NoteTypes.h" +#include "NoteDisplay.h" class PlayerState; /** @brief Row of GhostArrow Actors. */ @@ -15,6 +16,7 @@ public: virtual void DrawPrimitives(); void Load( const PlayerState* pPlayerState, float fYReverseOffset ); + void SetColumnRenderers(vector& renderers); void DidTapNote( int iCol, TapNoteScore tns, bool bBright ); void DidHoldNote( int iCol, HoldNoteScore hns, bool bBright ); diff --git a/src/NoteDisplay.cpp b/src/NoteDisplay.cpp index fca04b8b32..a9fe88813c 100644 --- a/src/NoteDisplay.cpp +++ b/src/NoteDisplay.cpp @@ -7,6 +7,7 @@ #include "ArrowEffects.h" #include "RageLog.h" #include "RageDisplay.h" +#include "ReceptorArrowRow.h" #include "ActorUtil.h" #include "Style.h" #include "PlayerState.h" @@ -341,8 +342,8 @@ bool NoteDisplay::IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTarge return true; } -bool NoteDisplay::DrawHoldsInRange(NoteDisplayRenderArgs const& args, - vector const& tap_set) +bool NoteDisplay::DrawHoldsInRange(CommonColumnRenderArgs const& args, + int column, vector const& tap_set) { bool any_upcoming = false; for(vector::const_iterator tapit= @@ -359,10 +360,10 @@ bool NoteDisplay::DrawHoldsInRange(NoteDisplayRenderArgs const& args, float throw_away; bool start_past_peak = false; bool end_past_peak = false; - float start_y = ArrowEffects::GetYOffset(m_pPlayerState, args.column, + float start_y = ArrowEffects::GetYOffset(m_pPlayerState, column, NoteRowToVisibleBeat(m_pPlayerState, start_row), throw_away, start_past_peak); - float end_y = ArrowEffects::GetYOffset(m_pPlayerState, args.column, + float end_y = ArrowEffects::GetYOffset(m_pPlayerState, column, NoteRowToVisibleBeat(m_pPlayerState, end_row), throw_away, end_past_peak); bool tail_visible = args.draw_pixels_after_targets <= end_y && @@ -385,19 +386,19 @@ bool NoteDisplay::DrawHoldsInRange(NoteDisplayRenderArgs const& args, const bool is_holding = tn.HoldResult.bHeld; if(hold_ghost_showing) { - args.ghost_row.SetHoldShowing(args.column, tn); + args.ghost_row->SetHoldShowing(column, tn); } - ASSERT_M(NoteRowToBeat(start_row) > -2000, ssprintf("%i %i %i", start_row, end_row, args.column)); + ASSERT_M(NoteRowToBeat(start_row) > -2000, ssprintf("%i %i %i", start_row, end_row, column)); bool in_selection_range = false; - if(args.selection_begin_marker != -1 && args.selection_end_marker != -1) + if(*args.selection_begin_marker != -1 && *args.selection_end_marker != -1) { - in_selection_range = (args.selection_begin_marker <= start_row && - end_row < args.selection_end_marker); + in_selection_range = (*args.selection_begin_marker <= start_row && + end_row < *args.selection_end_marker); } - DrawHold(tn, args.column, start_row, is_holding, result, + DrawHold(tn, column, start_row, is_holding, result, use_addition_coloring, in_selection_range ? args.selection_glow : args.fail_fade, m_fYReverseOffsetPixels, (float)args.draw_pixels_after_targets, @@ -411,8 +412,8 @@ bool NoteDisplay::DrawHoldsInRange(NoteDisplayRenderArgs const& args, return any_upcoming; } -bool NoteDisplay::DrawTapsInRange(NoteDisplayRenderArgs const& args, - vector const& tap_set) +bool NoteDisplay::DrawTapsInRange(CommonColumnRenderArgs const& args, + int column, vector const& tap_set) { bool any_upcoming= false; // draw notes from furthest to closest @@ -425,7 +426,7 @@ bool NoteDisplay::DrawTapsInRange(NoteDisplayRenderArgs const& args, // TRICKY: If boomerang is on, then all notes in the range // [first_row,last_row] aren't necessarily visible. // Test every note to make sure it's on screen before drawing. - if(!IsOnScreen(NoteRowToBeat(tap_row), args.column, + if(!IsOnScreen(NoteRowToBeat(tap_row), column, args.draw_pixels_after_targets, args.draw_pixels_before_targets)) { continue; // skip @@ -446,9 +447,9 @@ bool NoteDisplay::DrawTapsInRange(NoteDisplayRenderArgs const& args, bool hold_begins_on_this_beat = false; if(DrawHoldHeadForTapsOnSameRow()) { - for(int c2= 0; c2 < args.note_data.GetNumTracks(); ++c2) + for(int c2= 0; c2 < args.note_data->GetNumTracks(); ++c2) { - const TapNote &tmp = args.note_data.GetTapNote(c2, tap_row); + const TapNote &tmp = args.note_data->GetTapNote(c2, tap_row); if(tmp.type == TapNoteType_HoldHead && tmp.subType == TapNoteSubType_Hold) { @@ -462,9 +463,9 @@ bool NoteDisplay::DrawTapsInRange(NoteDisplayRenderArgs const& args, bool roll_begins_on_this_beat = false; if(DrawRollHeadForTapsOnSameRow()) { - for(int c2= 0; c2 < args.note_data.GetNumTracks(); ++c2) + for(int c2= 0; c2 < args.note_data->GetNumTracks(); ++c2) { - const TapNote &tmp = args.note_data.GetTapNote(c2, tap_row); + const TapNote &tmp = args.note_data->GetTapNote(c2, tap_row); if(tmp.type == TapNoteType_HoldHead && tmp.subType == TapNoteSubType_Roll) { @@ -475,16 +476,16 @@ bool NoteDisplay::DrawTapsInRange(NoteDisplayRenderArgs const& args, } bool in_selection_range = false; - if(args.selection_begin_marker != -1 && args.selection_end_marker != -1) + if(*args.selection_begin_marker != -1 && *args.selection_end_marker != -1) { - in_selection_range = args.selection_begin_marker <= tap_row && - tap_row < args.selection_end_marker; + in_selection_range = *args.selection_begin_marker <= tap_row && + tap_row < *args.selection_end_marker; } bool is_addition = (tn.source == TapNoteSource_Addition); bool hopo_possible = (tn.bHopoPossible); bool use_addition_coloring = is_addition || hopo_possible; - DrawTap(tn, args.column, NoteRowToVisibleBeat(m_pPlayerState, tap_row), + DrawTap(tn, column, NoteRowToVisibleBeat(m_pPlayerState, tap_row), hold_begins_on_this_beat, roll_begins_on_this_beat, use_addition_coloring, in_selection_range ? args.selection_glow : args.fail_fade, @@ -1064,6 +1065,64 @@ void NoteDisplay::DrawTap(const TapNote& tn, int iCol, float fBeat, pActor->PlayCommand( "UnsetAttack" ); } +void NoteColumnRenderer::DrawPrimitives() +{ + bool any_upcoming= false; + // Build lists of holds and taps for each player number, then pass those + // lists to the displays to draw. + // The vector in the NUM_PlayerNumber slot should stay empty, not worth + // optimizing it out. -Kyz + vector > holds(PLAYER_INVALID+1); + vector > taps(PLAYER_INVALID+1); + NoteData::TrackMap::const_iterator begin, end; + m_render_args->note_data->GetTapNoteRangeInclusive(m_column, + m_render_args->first_row, m_render_args->last_row+1, begin, end); + for(; begin != end; ++begin) + { + TapNote const& tn= begin->second; + switch(tn.type) + { + case TapNoteType_Empty: + continue; + case TapNoteType_Tap: + case TapNoteType_HoldTail: + case TapNoteType_Mine: + case TapNoteType_Lift: + case TapNoteType_Attack: + case TapNoteType_AutoKeysound: + case TapNoteType_Fake: + if(!tn.result.bHidden) + { + taps[tn.pn].push_back(begin); + } + break; + case TapNoteType_HoldHead: + if(tn.HoldResult.hns != HNS_Held) + { + holds[tn.pn].push_back(begin); + } + break; + } + } +#define DTS_INNER(pn, tap_set, draw_func, disp) \ + if(!tap_set[pn].empty()) \ + { \ + any_upcoming|= disp->draw_func(*m_render_args, m_column, tap_set[pn]); \ + } +#define DRAW_TAP_SET(tap_set, draw_func) \ + FOREACH_PlayerNumber(pn) \ + { \ + DTS_INNER(pn, tap_set, draw_func, m_displays[pn]); \ + } + DRAW_TAP_SET(holds, DrawHoldsInRange); + DTS_INNER(PLAYER_INVALID, holds, DrawHoldsInRange, m_displays[PLAYER_INVALID]); + DRAW_TAP_SET(taps, DrawTapsInRange); + DTS_INNER(PLAYER_INVALID, taps, DrawTapsInRange, m_displays[PLAYER_INVALID]); +#undef DTS_INNER +#undef DRAW_TAP_SET + m_render_args->receptor_row->SetNoteUpcoming(m_column, any_upcoming); +} + /* * (c) 2001-2006 Brian Bugh, Ben Nordstrom, Chris Danford, Steve Checkoway * All rights reserved. diff --git a/src/NoteDisplay.h b/src/NoteDisplay.h index 2bc3d0161b..3487be8d6c 100644 --- a/src/NoteDisplay.h +++ b/src/NoteDisplay.h @@ -1,15 +1,16 @@ #ifndef NOTE_DISPLAY_H #define NOTE_DISPLAY_H +#include "ActorFrame.h" #include "NoteData.h" #include "PlayerNumber.h" #include "GameInput.h" -class Actor; class Sprite; class Model; class PlayerState; class GhostArrowRow; +class ReceptorArrowRow; struct TapNote; struct HoldNoteResult; struct NoteMetricCache_t; @@ -87,28 +88,22 @@ const RString &ActiveTypeToString( ActiveType at ); // A little pod struct to carry the data the NoteField needs to pass to the // NoteDisplay during rendering. -struct NoteDisplayRenderArgs +struct CommonColumnRenderArgs { - NoteData const& note_data; - int column; + ReceptorArrowRow* receptor_row; + GhostArrowRow* ghost_row; + NoteData const* note_data; + int first_row; + int last_row; int draw_pixels_before_targets; int draw_pixels_after_targets; - int selection_begin_marker; - int selection_end_marker; + int* selection_begin_marker; + int* selection_end_marker; float selection_glow; float fail_fade; float fade_before_targets; - GhostArrowRow& ghost_row; -NoteDisplayRenderArgs(NoteData const& nd, int dpbt, int dpat, int sbm, - int sem, float sg, float ff, float fbt, GhostArrowRow& gr) - :note_data(nd), column(0), - draw_pixels_before_targets(dpbt), draw_pixels_after_targets(dpat), - selection_begin_marker(sbm), selection_end_marker(sem), - selection_glow(sg), fail_fade(ff), fade_before_targets(fbt), ghost_row(gr) - {} }; - /** @brief Draws TapNotes and HoldNotes. */ class NoteDisplay { @@ -122,9 +117,9 @@ public: bool IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const; - bool DrawHoldsInRange(NoteDisplayRenderArgs const& args, + bool DrawHoldsInRange(CommonColumnRenderArgs const& args, int column, vector const& tap_set); - bool DrawTapsInRange(NoteDisplayRenderArgs const& args, + bool DrawTapsInRange(CommonColumnRenderArgs const& args, int column, vector const& tap_set); /** * @brief Draw the TapNote onto the NoteField. @@ -184,6 +179,25 @@ private: float m_fYReverseOffsetPixels; }; +// So, this is a bit screwy, and it's all because routine forces rendering +// notes from different noteskins in the same column. +// NoteColumnRenderer exists to hold all the data needed for rendering a +// column and apply any transforms from that column's actor to the +// NoteDisplays that render the notes. +// NoteColumnRenderer is also used as a fake parent for the receptor and ghost +// actors so they can move with the rest of the column. I didn't use +// ActorProxy because the receptor/ghost actors need to pull in the parent +// state of their rows and the parent state of the column. -Kyz +class NoteColumnRenderer : public ActorFrame +{ + public: + NoteDisplay* m_displays[PLAYER_INVALID+1]; + CommonColumnRenderArgs* m_render_args; + int m_column; + + virtual void DrawPrimitives(); +}; + #endif /** diff --git a/src/NoteField.cpp b/src/NoteField.cpp index 094dabfe4d..1ff8fc1cf7 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -61,9 +61,13 @@ NoteField::NoteField() m_sprBeatBars.Load( THEME->GetPathG("NoteField","bars") ); m_sprBeatBars.StopAnimating(); + // I decided to do it this way because I don't want to dig through + // ScreenEdit to change all the places it touches the markers. -Kyz + m_ColumnRenderArgs.selection_begin_marker= &m_iBeginMarker; + m_ColumnRenderArgs.selection_end_marker= &m_iEndMarker; m_iBeginMarker = m_iEndMarker = -1; - m_fPercentFadeToFail = -1; + m_ColumnRenderArgs.fail_fade = -1; m_StepCallback.SetFromNil(); m_SetPressedCallback.SetFromNil(); @@ -166,6 +170,8 @@ void NoteField::CacheAllUsedNoteSkins() ASSERT_M( it != m_NoteDisplays.end(), sNoteSkinLower ); m_pDisplays[pn] = it->second; } + + InitColumnRenderers(); } void NoteField::Init( const PlayerState* pPlayerState, float fYReverseOffsetPixels ) @@ -186,7 +192,7 @@ void NoteField::Load( m_iDrawDistanceBeforeTargetsPixels = iDrawDistanceBeforeTargetsPixels; ASSERT( m_iDrawDistanceBeforeTargetsPixels >= m_iDrawDistanceAfterTargetsPixels ); - m_fPercentFadeToFail = -1; + m_ColumnRenderArgs.fail_fade = -1; //int i1 = m_pNoteData->GetNumTracks(); //int i2 = GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer; @@ -237,6 +243,27 @@ void NoteField::Load( ASSERT_M( it != m_NoteDisplays.end(), sNoteSkinLower ); m_pDisplays[pn] = it->second; } + InitColumnRenderers(); +} + +void NoteField::InitColumnRenderers() +{ + m_ColumnRenderArgs.receptor_row= &(m_pCurDisplay->m_ReceptorArrowRow); + m_ColumnRenderArgs.ghost_row= &(m_pCurDisplay->m_GhostArrowRow); + m_ColumnRenderArgs.note_data= m_pNoteData; + m_ColumnRenderers.resize(GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer); + for(size_t ncr= 0; ncr < m_ColumnRenderers.size(); ++ncr) + { + FOREACH_EnabledPlayer(pn) + { + m_ColumnRenderers[ncr].m_displays[pn]= &(m_pDisplays[pn]->display[ncr]); + } + m_ColumnRenderers[ncr].m_displays[PLAYER_INVALID]= &(m_pCurDisplay->display[ncr]); + m_ColumnRenderers[ncr].m_column= ncr; + m_ColumnRenderers[ncr].m_render_args= &m_ColumnRenderArgs; + } + m_pCurDisplay->m_ReceptorArrowRow.SetColumnRenderers(m_ColumnRenderers); + m_pCurDisplay->m_GhostArrowRow.SetColumnRenderers(m_ColumnRenderers); } void NoteField::Update( float fDeltaTime ) @@ -248,6 +275,11 @@ void NoteField::Update( float fDeltaTime ) ActorFrame::Update( fDeltaTime ); + for(size_t c= 0; c < m_ColumnRenderers.size(); ++c) + { + m_ColumnRenderers[c].Update(fDeltaTime); + } + // update m_fBoardOffsetPixels, m_fCurrentBeatLastUpdate, m_fYPosCurrentBeatLastUpdate const float fCurrentBeat = m_pPlayerState->GetDisplayedPosition().m_fSongBeat; bool bTweeningOn = m_sprBoard->GetCurrentDiffuseAlpha() >= 0.98 && m_sprBoard->GetCurrentDiffuseAlpha() < 1.00; // HACK @@ -273,11 +305,11 @@ void NoteField::Update( float fDeltaTime ) cur->m_ReceptorArrowRow.Update( fDeltaTime ); cur->m_GhostArrowRow.Update( fDeltaTime ); - if( m_fPercentFadeToFail >= 0 ) - m_fPercentFadeToFail = min( m_fPercentFadeToFail + fDeltaTime/FADE_FAIL_TIME, 1 ); + if( m_ColumnRenderArgs.fail_fade >= 0 ) + m_ColumnRenderArgs.fail_fade = min( m_ColumnRenderArgs.fail_fade + fDeltaTime/FADE_FAIL_TIME, 1 ); // Update fade to failed - m_pCurDisplay->m_ReceptorArrowRow.SetFadeToFailPercent( m_fPercentFadeToFail ); + m_pCurDisplay->m_ReceptorArrowRow.SetFadeToFailPercent( m_ColumnRenderArgs.fail_fade ); NoteDisplay::Update( fDeltaTime ); /* Update all NoteDisplays. Hack: We need to call this once per frame, not @@ -829,41 +861,41 @@ void NoteField::DrawPrimitives() const PlayerOptions ¤t_po = m_pPlayerState->m_PlayerOptions.GetCurrent(); // Adjust draw range depending on some effects - int iDrawDistanceAfterTargetsPixels = m_iDrawDistanceAfterTargetsPixels; + m_ColumnRenderArgs.draw_pixels_after_targets= m_iDrawDistanceAfterTargetsPixels; // HACK: If boomerang and centered are on, then we want to draw much // earlier so that the notes don't pop on screen. float fCenteredTimesBoomerang = current_po.m_fScrolls[PlayerOptions::SCROLL_CENTERED] * current_po.m_fAccels[PlayerOptions::ACCEL_BOOMERANG]; - iDrawDistanceAfterTargetsPixels += int(SCALE( fCenteredTimesBoomerang, 0.f, 1.f, 0.f, -SCREEN_HEIGHT/2 )); - int iDrawDistanceBeforeTargetsPixels = m_iDrawDistanceBeforeTargetsPixels; + m_ColumnRenderArgs.draw_pixels_after_targets += int(SCALE( fCenteredTimesBoomerang, 0.f, 1.f, 0.f, -SCREEN_HEIGHT/2 )); + m_ColumnRenderArgs.draw_pixels_before_targets = m_iDrawDistanceBeforeTargetsPixels; float fDrawScale = 1; fDrawScale *= 1 + 0.5f * fabsf( current_po.m_fPerspectiveTilt ); fDrawScale *= 1 + fabsf( current_po.m_fEffects[PlayerOptions::EFFECT_MINI] ); - iDrawDistanceAfterTargetsPixels = (int)(iDrawDistanceAfterTargetsPixels * fDrawScale); - iDrawDistanceBeforeTargetsPixels = (int)(iDrawDistanceBeforeTargetsPixels * fDrawScale); + m_ColumnRenderArgs.draw_pixels_after_targets = (int)(m_ColumnRenderArgs.draw_pixels_after_targets * fDrawScale); + m_ColumnRenderArgs.draw_pixels_before_targets = (int)(m_ColumnRenderArgs.draw_pixels_before_targets * fDrawScale); // Probe for first and last notes on the screen - float fFirstBeatToDraw = FindFirstDisplayedBeat( m_pPlayerState, iDrawDistanceAfterTargetsPixels ); - float fLastBeatToDraw = FindLastDisplayedBeat( m_pPlayerState, iDrawDistanceBeforeTargetsPixels ); + float fFirstBeatToDraw = FindFirstDisplayedBeat( m_pPlayerState, m_ColumnRenderArgs.draw_pixels_after_targets ); + float fLastBeatToDraw = FindLastDisplayedBeat( m_pPlayerState, m_ColumnRenderArgs.draw_pixels_before_targets ); m_pPlayerState->m_fLastDrawnBeat = fLastBeatToDraw; - const int iFirstRowToDraw = BeatToNoteRow(fFirstBeatToDraw); - const int iLastRowToDraw = BeatToNoteRow(fLastBeatToDraw); + m_ColumnRenderArgs.first_row = BeatToNoteRow(fFirstBeatToDraw); + m_ColumnRenderArgs.last_row = BeatToNoteRow(fLastBeatToDraw); //LOG->Trace( "start = %f.1, end = %f.1", fFirstBeatToDraw-fSongBeat, fLastBeatToDraw-fSongBeat ); - //LOG->Trace( "Drawing elements %d through %d", iFirstRowToDraw, iLastRowToDraw ); + //LOG->Trace( "Drawing elements %d through %d", m_ColumnRenderArgs.first_row, m_ColumnRenderArgs.last_row ); -#define IS_ON_SCREEN( fBeat ) ( fFirstBeatToDraw <= (fBeat) && (fBeat) <= fLastBeatToDraw && IsOnScreen( fBeat, 0, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels ) ) +#define IS_ON_SCREEN( fBeat ) ( fFirstBeatToDraw <= (fBeat) && (fBeat) <= fLastBeatToDraw && IsOnScreen( fBeat, 0, m_ColumnRenderArgs.draw_pixels_after_targets, m_ColumnRenderArgs.draw_pixels_before_targets ) ) // Draw board if( SHOW_BOARD ) { - DrawBoard( iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels ); + DrawBoard( m_ColumnRenderArgs.draw_pixels_after_targets, m_ColumnRenderArgs.draw_pixels_before_targets ); } // Draw Receptors @@ -886,7 +918,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < tSigs.size(); i++) { const TimeSignatureSegment *ts = ToTimeSignature(tSigs[i]); - int iSegmentEndRow = (i + 1 == tSigs.size()) ? iLastRowToDraw : tSigs[i+1]->GetRow(); + int iSegmentEndRow = (i + 1 == tSigs.size()) ? m_ColumnRenderArgs.last_row : tSigs[i+1]->GetRow(); // beat bars every 16th note int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)ts->GetDen()) / 4 ) / 4; @@ -929,7 +961,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_SCROLL]->size(); i++) { ScrollSegment *seg = ToScroll( segs[SEGMENT_SCROLL]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -941,7 +973,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_BPM]->size(); i++) { const BPMSegment *seg = ToBPM( segs[SEGMENT_BPM]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -953,7 +985,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_STOP]->size(); i++) { const StopSegment *seg = ToStop( segs[SEGMENT_STOP]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -965,7 +997,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_DELAY]->size(); i++) { const DelaySegment *seg = ToDelay( segs[SEGMENT_DELAY]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -977,7 +1009,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_WARP]->size(); i++) { const WarpSegment *seg = ToWarp( segs[SEGMENT_WARP]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -989,7 +1021,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_TIME_SIG]->size(); i++) { const TimeSignatureSegment *seg = ToTimeSignature( segs[SEGMENT_TIME_SIG]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -1001,7 +1033,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_TICKCOUNT]->size(); i++) { const TickcountSegment *seg = ToTickcount( segs[SEGMENT_TICKCOUNT]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -1013,7 +1045,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_COMBO]->size(); i++) { const ComboSegment *seg = ToCombo( segs[SEGMENT_COMBO]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -1025,7 +1057,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_LABEL]->size(); i++) { const LabelSegment *seg = ToLabel( segs[SEGMENT_LABEL]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -1037,7 +1069,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_SPEED]->size(); i++) { const SpeedSegment *seg = ToSpeed( segs[SEGMENT_SPEED]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -1050,7 +1082,7 @@ void NoteField::DrawPrimitives() for (i = 0; i < segs[SEGMENT_FAKE]->size(); i++) { const FakeSegment *seg = ToFake( segs[SEGMENT_FAKE]->at(i) ); - if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) + if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) { float fBeat = seg->GetBeat(); if( IS_ON_SCREEN(fBeat) ) @@ -1070,8 +1102,8 @@ void NoteField::DrawPrimitives() float fSecond = a->fStartSecond; float fBeat = timing.GetBeatFromElapsedTime( fSecond ); - if( BeatToNoteRow(fBeat) >= iFirstRowToDraw && - BeatToNoteRow(fBeat) <= iLastRowToDraw) + if( BeatToNoteRow(fBeat) >= m_ColumnRenderArgs.first_row && + BeatToNoteRow(fBeat) <= m_ColumnRenderArgs.last_row) { if( IS_ON_SCREEN(fBeat) ) DrawAttackText( fBeat, *a ); @@ -1089,8 +1121,8 @@ void NoteField::DrawPrimitives() FOREACH_CONST(Attack, attacks, a) { float fBeat = timing.GetBeatFromElapsedTime(a->fStartSecond); - if (BeatToNoteRow(fBeat) >= iFirstRowToDraw && - BeatToNoteRow(fBeat) <= iLastRowToDraw && + if (BeatToNoteRow(fBeat) >= m_ColumnRenderArgs.first_row && + BeatToNoteRow(fBeat) <= m_ColumnRenderArgs.last_row && IS_ON_SCREEN(fBeat)) { this->DrawAttackText(fBeat, *a); @@ -1174,20 +1206,20 @@ void NoteField::DrawPrimitives() { int iBegin = m_iBeginMarker; int iEnd = m_iEndMarker; - CLAMP( iBegin, iFirstRowToDraw, iLastRowToDraw ); - CLAMP( iEnd, iFirstRowToDraw, iLastRowToDraw ); + CLAMP( iBegin, m_ColumnRenderArgs.first_row, m_ColumnRenderArgs.last_row ); + CLAMP( iEnd, m_ColumnRenderArgs.first_row, m_ColumnRenderArgs.last_row ); DrawAreaHighlight( iBegin, iEnd ); } else if( m_iBeginMarker != -1 ) { - if( m_iBeginMarker >= iFirstRowToDraw && - m_iBeginMarker <= iLastRowToDraw ) + if( m_iBeginMarker >= m_ColumnRenderArgs.first_row && + m_iBeginMarker <= m_ColumnRenderArgs.last_row ) DrawMarkerBar( m_iBeginMarker ); } else if( m_iEndMarker != -1 ) { - if( m_iEndMarker >= iFirstRowToDraw && - m_iEndMarker <= iLastRowToDraw ) + if( m_iEndMarker >= m_ColumnRenderArgs.first_row && + m_iEndMarker <= m_ColumnRenderArgs.last_row ) DrawMarkerBar( m_iEndMarker ); } } @@ -1196,76 +1228,19 @@ void NoteField::DrawPrimitives() // Draw the arrows in order of column. This minimizes texture switches and // lets us draw in big batches. - float fSelectedRangeGlow = SCALE( RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f ); - const Style* pStyle = GAMESTATE->GetCurrentStyle(); ASSERT_M(m_pNoteData->GetNumTracks() == GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer, ssprintf("NumTracks %d != ColsPerPlayer %d",m_pNoteData->GetNumTracks(), GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer)); - NoteDisplayRenderArgs display_args(*m_pNoteData, - iDrawDistanceBeforeTargetsPixels, - iDrawDistanceAfterTargetsPixels, m_iBeginMarker, m_iEndMarker, - fSelectedRangeGlow, m_fPercentFadeToFail, FADE_BEFORE_TARGETS_PERCENT, - m_pCurDisplay->m_GhostArrowRow); + m_ColumnRenderArgs.selection_glow= SCALE( + RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f); + m_ColumnRenderArgs.fade_before_targets= FADE_BEFORE_TARGETS_PERCENT; + for( int j=0; jGetNumTracks(); j++ ) // for each arrow column { const int c = pStyle->m_iColumnDrawOrder[j]; - display_args.column= c; - bool any_upcoming= false; - // Build lists of holds and taps for each player number, then pass those - // lists to the displays to draw. - // The vector in the NUM_PlayerNumber slot should stay empty, not worth - // optimizing it out. -Kyz - vector > holds(PLAYER_INVALID+1); - vector > taps(PLAYER_INVALID+1); - NoteData::TrackMap::const_iterator begin, end; - m_pNoteData->GetTapNoteRangeInclusive(c, iFirstRowToDraw, - iLastRowToDraw+1, begin, end); - for(; begin != end; ++begin) - { - TapNote const& tn= begin->second; - switch(tn.type) - { - case TapNoteType_Empty: - continue; - case TapNoteType_Tap: - case TapNoteType_HoldTail: - case TapNoteType_Mine: - case TapNoteType_Lift: - case TapNoteType_Attack: - case TapNoteType_AutoKeysound: - case TapNoteType_Fake: - if(!tn.result.bHidden) - { - taps[tn.pn].push_back(begin); - } - break; - case TapNoteType_HoldHead: - if(tn.HoldResult.hns != HNS_Held) - { - holds[tn.pn].push_back(begin); - } - break; - } - } -#define DTS_INNER(pn, tap_set, draw_func, disp) \ - if(!tap_set[pn].empty()) \ - { \ - any_upcoming|= disp->display[c].draw_func(display_args, tap_set[pn]); \ - } -#define DRAW_TAP_SET(tap_set, draw_func) \ - FOREACH_PlayerNumber(pn) \ - { \ - DTS_INNER(pn, tap_set, draw_func, m_pDisplays[pn]); \ - } - DRAW_TAP_SET(holds, DrawHoldsInRange); - DTS_INNER(PLAYER_INVALID, holds, DrawHoldsInRange, m_pCurDisplay); - DRAW_TAP_SET(taps, DrawTapsInRange); - DTS_INNER(PLAYER_INVALID, taps, DrawTapsInRange, m_pCurDisplay); -#undef DTS_INNER -#undef DRAW_TAP_SET - cur->m_ReceptorArrowRow.SetNoteUpcoming(c, any_upcoming); + m_ColumnRenderers[c].Draw(); } cur->m_GhostArrowRow.Draw(); @@ -1273,7 +1248,7 @@ void NoteField::DrawPrimitives() void NoteField::FadeToFail() { - m_fPercentFadeToFail = max( 0.0f, m_fPercentFadeToFail ); // this will slowly increase every Update() + m_ColumnRenderArgs.fail_fade = max( 0.0f, m_ColumnRenderArgs.fail_fade ); // this will slowly increase every Update() // don't fade all over again if this is called twice } @@ -1471,6 +1446,17 @@ public: return 0; } + static int GetColumnActors(T* p, lua_State* L) + { + lua_createtable(L, p->m_ColumnRenderers.size(), 0); + for(size_t i= 0; i < p->m_ColumnRenderers.size(); ++i) + { + p->m_ColumnRenderers[i].PushSelf(L); + lua_rawseti(L, -2, i+1); + } + return 1; + } + LunaNoteField() { ADD_METHOD(SetStepCallback); @@ -1481,6 +1467,7 @@ public: ADD_METHOD(SetPressed); ADD_METHOD(DidTapNote); ADD_METHOD(DidHoldNote); + ADD_METHOD(GetColumnActors); } }; diff --git a/src/NoteField.h b/src/NoteField.h index cfdf24be04..c10dcbd435 100644 --- a/src/NoteField.h +++ b/src/NoteField.h @@ -29,6 +29,8 @@ public: int iDrawDistanceBeforeTargetsPixels ); virtual void Unload(); + void InitColumnRenderers(); + virtual void HandleMessage( const Message &msg ); // This is done automatically by Init(), but can be re-called explicitly if the @@ -55,6 +57,10 @@ public: int m_iBeginMarker, m_iEndMarker; // only used with MODE_EDIT + // m_ColumnRenderers belongs in the protected section, but it's here in + // public so that the Lua API can access it. -Kyz + vector m_ColumnRenderers; + protected: void CacheNoteSkin( const RString &sNoteSkin ); void UncacheNoteSkin( const RString &sNoteSkin ); @@ -84,8 +90,6 @@ protected: const NoteData *m_pNoteData; - float m_fPercentFadeToFail; // -1 if not fading to fail - const PlayerState* m_pPlayerState; int m_iDrawDistanceAfterTargetsPixels; // this should be a negative number int m_iDrawDistanceBeforeTargetsPixels; // this should be a positive number @@ -101,6 +105,8 @@ protected: ~NoteDisplayCols() { delete [] display; } }; + CommonColumnRenderArgs m_ColumnRenderArgs; + /* All loaded note displays, mapped by their name. */ map m_NoteDisplays; NoteDisplayCols *m_pCurDisplay; diff --git a/src/ReceptorArrowRow.cpp b/src/ReceptorArrowRow.cpp index fd6d2deb31..8c7f6dc54c 100644 --- a/src/ReceptorArrowRow.cpp +++ b/src/ReceptorArrowRow.cpp @@ -30,6 +30,15 @@ void ReceptorArrowRow::Load( const PlayerState* pPlayerState, float fYReverseOff } } +void ReceptorArrowRow::SetColumnRenderers(vector& renderers) +{ + ASSERT_M(renderers.size() == m_ReceptorArrow.size(), "Notefield has different number of columns than receptor row."); + for(size_t c= 0; c < m_ReceptorArrow.size(); ++c) + { + m_ReceptorArrow[c]->SetFakeParent(&(renderers[c])); + } +} + ReceptorArrowRow::~ReceptorArrowRow() { for( unsigned i = 0; i < m_ReceptorArrow.size(); ++i ) diff --git a/src/ReceptorArrowRow.h b/src/ReceptorArrowRow.h index 1b93047386..ac6f081643 100644 --- a/src/ReceptorArrowRow.h +++ b/src/ReceptorArrowRow.h @@ -4,6 +4,7 @@ #include "ReceptorArrow.h" #include "ActorFrame.h" #include "GameConstantsAndTypes.h" +#include "NoteDisplay.h" class PlayerState; /** @brief A row of ReceptorArrow objects. */ @@ -16,6 +17,7 @@ public: virtual void DrawPrimitives(); void Load( const PlayerState* pPlayerState, float fYReverseOffset ); + void SetColumnRenderers(vector& renderers); void Step( int iCol, TapNoteScore score ); void SetPressed( int iCol );