bool StepsCriteria::Matches( const Song *pSong, const Steps *pSteps ) const { if( m_difficulty != Difficulty_Invalid && pSteps->GetDifficulty() != m_difficulty ) return false; if( m_iLowMeter != -1 && pSteps->GetMeter() < m_iLowMeter ) return false; if( m_iHighMeter != -1 && pSteps->GetMeter() > m_iHighMeter ) return false; if( m_st != StepsType_Invalid && pSteps->m_StepsType != m_st ) return false; switch( m_Locked ) { DEFAULT_FAIL(m_Locked); case Locked_Locked: if( UNLOCKMAN && !UNLOCKMAN->StepsIsLocked(pSong,pSteps) ) return false; break; case Locked_Unlocked: if( UNLOCKMAN && UNLOCKMAN->StepsIsLocked(pSong,pSteps) ) return false; break; case Locked_DontCare: break; } return true; }
// TODO: Move Course initialization after PROFILEMAN is created static void CourseSortSongs( SongSort sort, vector<Song*> &vpPossibleSongs, RandomGen &rnd ) { switch( sort ) { DEFAULT_FAIL(sort); case SongSort_Randomize: random_shuffle( vpPossibleSongs.begin(), vpPossibleSongs.end(), rnd ); break; case SongSort_MostPlays: if( PROFILEMAN ) SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), true ); // descending break; case SongSort_FewestPlays: if( PROFILEMAN ) SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), false ); // ascending break; case SongSort_TopGrades: if( PROFILEMAN ) SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, true ); // descending break; case SongSort_LowestGrades: if( PROFILEMAN ) SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, false ); // ascending break; } }
int ScoreKeeperNormal::TapNoteScoreToGradePoints( TapNoteScore tns, bool bBeginner ) { if( !GAMESTATE->ShowW1() && tns == TNS_W1 ) tns = TNS_W2; /* This is used for Oni percentage displays. Grading values are currently in * StageStats::GetGrade. */ int iWeight = 0; switch( tns ) { DEFAULT_FAIL( tns ); case TNS_None: iWeight = 0; break; case TNS_AvoidMine: iWeight = 0; break; case TNS_HitMine: iWeight = g_iGradeWeight.GetValue(SE_HitMine); break; case TNS_Miss: iWeight = g_iGradeWeight.GetValue(SE_Miss); break; case TNS_W5: iWeight = g_iGradeWeight.GetValue(SE_W5); break; case TNS_W4: iWeight = g_iGradeWeight.GetValue(SE_W4); break; case TNS_W3: iWeight = g_iGradeWeight.GetValue(SE_W3); break; case TNS_W2: iWeight = g_iGradeWeight.GetValue(SE_W2); break; case TNS_W1: iWeight = g_iGradeWeight.GetValue(SE_W1); break; case TNS_CheckpointHit: iWeight = g_iGradeWeight.GetValue(SE_CheckpointHit); break; case TNS_CheckpointMiss:iWeight = g_iGradeWeight.GetValue(SE_CheckpointMiss); break; } if( bBeginner && PREFSMAN->m_bMercifulBeginner ) iWeight = max( 0, iWeight ); return iWeight; }
bool RageDisplay::SaveScreenshot( RString sPath, GraphicsFileFormat format ) { RageTimer timer; RageSurface *surface = this->CreateScreenshot(); // LOG->Trace( "CreateScreenshot took %f seconds", timer.GetDeltaTime() ); /* Unless we're in lossless, resize the image to 640x480. If we're saving lossy, * there's no sense in saving 1280x960 screenshots, and we don't want to output * screenshots in a strange (non-1) sample aspect ratio. */ if( format != SAVE_LOSSLESS && format != SAVE_LOSSLESS_SENSIBLE ) { // Maintain the DAR. ASSERT( GetActualVideoModeParams().fDisplayAspectRatio > 0 ); int iHeight = 480; // This used to be lrintf. However, lrintf causes odd resolutions like // 639x480 (4:3) and 853x480 (16:9). ceilf gives correct values. -aj int iWidth = static_cast<int>(ceilf( iHeight * GetActualVideoModeParams().fDisplayAspectRatio )); timer.Touch(); RageSurfaceUtils::Zoom( surface, iWidth, iHeight ); // LOG->Trace( "%ix%i -> %ix%i (%.3f) in %f seconds", surface->w, surface->h, iWidth, iHeight, GetActualVideoModeParams().fDisplayAspectRatio, timer.GetDeltaTime() ); } RageFile out; if( !out.Open( sPath, RageFile::WRITE ) ) { LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() ); SAFE_DELETE( surface ); return false; } bool bSuccess = false; timer.Touch(); RString strError = ""; switch( format ) { case SAVE_LOSSLESS: bSuccess = RageSurfaceUtils::SaveBMP( surface, out ); break; case SAVE_LOSSLESS_SENSIBLE: bSuccess = RageSurfaceUtils::SavePNG( surface, out, strError ); break; case SAVE_LOSSY_LOW_QUAL: bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, false ); break; case SAVE_LOSSY_HIGH_QUAL: bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, true ); break; DEFAULT_FAIL( format ); } // LOG->Trace( "Saving Screenshot file took %f seconds.", timer.GetDeltaTime() ); SAFE_DELETE( surface ); if( !bSuccess ) { LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() ); return false; } return true; }
void LifeMeterBar::ChangeLife( TapNoteScore score ) { float fDeltaLife=0.f; switch( score ) { DEFAULT_FAIL( score ); case TNS_W1: fDeltaLife = m_fLifePercentChange.GetValue(SE_W1); break; case TNS_W2: fDeltaLife = m_fLifePercentChange.GetValue(SE_W2); break; case TNS_W3: fDeltaLife = m_fLifePercentChange.GetValue(SE_W3); break; case TNS_W4: fDeltaLife = m_fLifePercentChange.GetValue(SE_W4); break; case TNS_W5: fDeltaLife = m_fLifePercentChange.GetValue(SE_W5); break; case TNS_Miss: fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss); break; case TNS_HitMine: fDeltaLife = m_fLifePercentChange.GetValue(SE_HitMine); break; case TNS_None: fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss); break; case TNS_CheckpointHit: fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointHit); break; case TNS_CheckpointMiss:fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointMiss); break; } // this was previously if( IsHot() && score < TNS_GOOD ) in 3.9... -freem if( IsHot() && fDeltaLife < 0 ) fDeltaLife = min( fDeltaLife, -0.10f ); // make it take a while to get back to "hot" switch( GAMESTATE->m_SongOptions.GetSong().m_DrainType ) { DEFAULT_FAIL( GAMESTATE->m_SongOptions.GetSong().m_DrainType ); case SongOptions::DRAIN_NORMAL: break; case SongOptions::DRAIN_NO_RECOVER: fDeltaLife = min( fDeltaLife, 0 ); break; case SongOptions::DRAIN_SUDDEN_DEATH: if( score < MIN_STAY_ALIVE ) fDeltaLife = -1.0f; else fDeltaLife = 0; break; } ChangeLife( fDeltaLife ); }
void LifeMeterBar::ChangeLife( TapNoteScore score ) { float fDeltaLife=0.f; switch( score ) { DEFAULT_FAIL( score ); case TNS_W1: fDeltaLife = m_fLifePercentChange.GetValue(SE_W1); break; case TNS_W2: fDeltaLife = m_fLifePercentChange.GetValue(SE_W2); break; case TNS_W3: fDeltaLife = m_fLifePercentChange.GetValue(SE_W3); break; case TNS_W4: fDeltaLife = m_fLifePercentChange.GetValue(SE_W4); break; case TNS_W5: fDeltaLife = m_fLifePercentChange.GetValue(SE_W5); break; case TNS_Miss: fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss); break; case TNS_HitMine: fDeltaLife = m_fLifePercentChange.GetValue(SE_HitMine); break; case TNS_None: fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss); break; case TNS_CheckpointHit: fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointHit); break; case TNS_CheckpointMiss:fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointMiss); break; } // this was previously if( IsHot() && score < TNS_GOOD ) in 3.9... -freem if(PREFSMAN->m_HarshHotLifePenalty && IsHot() && fDeltaLife < 0) fDeltaLife = min( fDeltaLife, -0.10f ); // make it take a while to get back to "hot" switch(m_pPlayerState->m_PlayerOptions.GetSong().m_DrainType) { DEFAULT_FAIL(m_pPlayerState->m_PlayerOptions.GetSong().m_DrainType); case DrainType_Normal: break; case DrainType_NoRecover: fDeltaLife = min( fDeltaLife, 0 ); break; case DrainType_SuddenDeath: if( score < MIN_STAY_ALIVE ) fDeltaLife = -1.0f; else fDeltaLife = 0; break; } ChangeLife( fDeltaLife ); }
int ScoreKeeperNormal::HoldNoteScoreToGradePoints( HoldNoteScore hns, bool bBeginner ) { int iWeight = 0; switch( hns ) { DEFAULT_FAIL( hns ); case HNS_None: iWeight = 0; break; case HNS_LetGo: iWeight = g_iGradeWeight.GetValue(SE_LetGo); break; case HNS_Held: iWeight = g_iGradeWeight.GetValue(SE_Held); break; case HNS_Missed: iWeight = g_iGradeWeight.GetValue(SE_Missed); break; } if( bBeginner && PREFSMAN->m_bMercifulBeginner ) iWeight = max( 0, iWeight ); return iWeight; }
static void SuperMeterPercentChangeInit( size_t /*ScoreEvent*/ i, RString &sNameOut, float &defaultValueOut ) { ScoreEvent ci = (ScoreEvent)i; sNameOut = "SuperMeterPercentChange" + ScoreEventToString( ci ); switch(ci) { case SE_CheckpointHit: defaultValueOut = +0.05f; break; case SE_W1: defaultValueOut = +0.05f; break; case SE_W2: defaultValueOut = +0.04f; break; case SE_W3: defaultValueOut = +0.02f; break; case SE_W4: defaultValueOut = +0.00f; break; case SE_W5: defaultValueOut = +0.00f; break; case SE_Miss: defaultValueOut = -0.20f; break; case SE_HitMine: defaultValueOut = -0.40f; break; case SE_CheckpointMiss: defaultValueOut = -0.20f; break; case SE_Held: defaultValueOut = +0.04f; break; case SE_LetGo: defaultValueOut = -0.20f; break; DEFAULT_FAIL(ci); } }
void ScoreKeeperRave::HandleTapRowScore( const NoteData &nd, int iRow ) { TapNoteScore scoreOfLastTap; int iNumTapsInRow; float fPercentToMove = 0.0f; GetScoreOfLastTapInRow( nd, iRow, scoreOfLastTap, iNumTapsInRow ); if( iNumTapsInRow <= 0 ) return; switch( scoreOfLastTap ) { DEFAULT_FAIL( scoreOfLastTap ); case TNS_W1: fPercentToMove = g_fSuperMeterPercentChange[SE_W1]; break; case TNS_W2: fPercentToMove = g_fSuperMeterPercentChange[SE_W2]; break; case TNS_W3: fPercentToMove = g_fSuperMeterPercentChange[SE_W3]; break; case TNS_W4: fPercentToMove = g_fSuperMeterPercentChange[SE_W4]; break; case TNS_W5: fPercentToMove = g_fSuperMeterPercentChange[SE_W5]; break; case TNS_Miss: fPercentToMove = g_fSuperMeterPercentChange[SE_Miss]; break; } AddSuperMeterDelta( fPercentToMove ); }
FOREACH_CONST( TrailEntry, m_vEntries, e ) { if( e->bSecret ) { AddTo.Add( -1 ); continue; } Song *pSong = e->pSong; ASSERT( pSong != NULL ); switch( pSong->m_DisplayBPMType ) { case DISPLAY_BPM_ACTUAL: case DISPLAY_BPM_SPECIFIED: pSong->GetDisplayBpms( AddTo ); break; case DISPLAY_BPM_RANDOM: AddTo.Add( -1 ); break; DEFAULT_FAIL( pSong->m_DisplayBPMType ); } }
float ArrowEffects::GetXPos( const PlayerState* pPlayerState, int iColNum, float fYOffset ) { float fPixelOffsetFromCenter = 0; // fill this in below const Style* pStyle = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber); const float* fEffects = curr_options->m_fEffects; // TODO: Don't index by PlayerNumber. const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pPlayerState->m_PlayerNumber]; PerPlayerData &data = g_EffectData[pPlayerState->m_PlayerNumber]; if( fEffects[PlayerOptions::EFFECT_TORNADO] != 0 ) { const float fRealPixelOffset = pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom; const float fPositionBetween = SCALE( fRealPixelOffset, data.m_fMinTornadoX[iColNum], data.m_fMaxTornadoX[iColNum], TORNADO_POSITION_SCALE_TO_LOW, TORNADO_POSITION_SCALE_TO_HIGH ); float fRads = acosf( fPositionBetween ); fRads += fYOffset * TORNADO_OFFSET_FREQUENCY / SCREEN_HEIGHT; const float fAdjustedPixelOffset = SCALE( RageFastCos(fRads), TORNADO_OFFSET_SCALE_FROM_LOW, TORNADO_OFFSET_SCALE_FROM_HIGH, data.m_fMinTornadoX[iColNum], data.m_fMaxTornadoX[iColNum] ); fPixelOffsetFromCenter += (fAdjustedPixelOffset - fRealPixelOffset) * fEffects[PlayerOptions::EFFECT_TORNADO]; } if( fEffects[PlayerOptions::EFFECT_DRUNK] != 0 ) fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_DRUNK] * ( RageFastCos( RageTimer::GetTimeSinceStartFast() + iColNum*DRUNK_COLUMN_FREQUENCY + fYOffset*DRUNK_OFFSET_FREQUENCY/SCREEN_HEIGHT) * ARROW_SIZE*DRUNK_ARROW_MAGNITUDE ); if( fEffects[PlayerOptions::EFFECT_FLIP] != 0 ) { const int iFirstCol = 0; const int iLastCol = pStyle->m_iColsPerPlayer-1; const int iNewCol = SCALE( iColNum, iFirstCol, iLastCol, iLastCol, iFirstCol ); const float fOldPixelOffset = pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom; const float fNewPixelOffset = pCols[iNewCol].fXOffset * pPlayerState->m_NotefieldZoom; const float fDistance = fNewPixelOffset - fOldPixelOffset; fPixelOffsetFromCenter += fDistance * fEffects[PlayerOptions::EFFECT_FLIP]; } if( fEffects[PlayerOptions::EFFECT_INVERT] != 0 ) fPixelOffsetFromCenter += data.m_fInvertDistance[iColNum] * fEffects[PlayerOptions::EFFECT_INVERT]; if( fEffects[PlayerOptions::EFFECT_BEAT] != 0 ) { const float fShift = data.m_fBeatFactor*RageFastSin( fYOffset / BEAT_OFFSET_HEIGHT + PI/BEAT_PI_HEIGHT ); fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_BEAT] * fShift; } if( fEffects[PlayerOptions::EFFECT_XMODE] != 0 ) { // based off of code by v1toko for StepNXA, except it should work on // any gametype now. switch( pStyle->m_StyleType ) { case StyleType_OnePlayerTwoSides: case StyleType_TwoPlayersSharedSides: // fall through? { // find the middle, and split based on iColNum // it's unknown if this will work for routine. const int iMiddleColumn = static_cast<int>(floor(pStyle->m_iColsPerPlayer/2.0f)); if( iColNum > iMiddleColumn-1 ) fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*-(fYOffset); else fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*fYOffset; } break; case StyleType_OnePlayerOneSide: case StyleType_TwoPlayersTwoSides: // fall through { // the code was the same for both of these cases in StepNXA. if( pPlayerState->m_PlayerNumber == PLAYER_2 ) fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*-(fYOffset); else fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*fYOffset; } break; case StyleType_FourPlayersFourSides: // fall through { // the code was the same for both of these cases in StepNXA. if (pPlayerState->m_PlayerNumber == PLAYER_2) fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE] * -(fYOffset); else fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE] * fYOffset; } break; DEFAULT_FAIL(pStyle->m_StyleType); } } fPixelOffsetFromCenter += pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom; if( fEffects[PlayerOptions::EFFECT_TINY] != 0 ) { // Allow Tiny to pull tracks together, but not to push them apart. float fTinyPercent = fEffects[PlayerOptions::EFFECT_TINY]; fTinyPercent = min( powf(TINY_PERCENT_BASE, fTinyPercent), (float)TINY_PERCENT_GATE ); fPixelOffsetFromCenter *= fTinyPercent; } return fPixelOffsetFromCenter; }
bool SongCriteria::Matches( const Song *pSong ) const { if( !m_sGroupName.empty() && m_sGroupName != pSong->m_sGroupName ) return false; if( UNLOCKMAN->SongIsLocked(pSong) & LOCKED_DISABLED ) return false; if( m_bUseSongGenreAllowedList ) { if( find(m_vsSongGenreAllowedList.begin(),m_vsSongGenreAllowedList.end(),pSong->m_sGenre) == m_vsSongGenreAllowedList.end() ) return false; } switch( m_Selectable ) { DEFAULT_FAIL(m_Selectable); case Selectable_Yes: if( UNLOCKMAN && UNLOCKMAN->SongIsLocked(pSong) & LOCKED_SELECTABLE ) return false; break; case Selectable_No: if( UNLOCKMAN && !(UNLOCKMAN->SongIsLocked(pSong) & LOCKED_SELECTABLE) ) return false; break; case Selectable_DontCare: break; } if( m_bUseSongAllowedList ) { if( find(m_vpSongAllowedList.begin(),m_vpSongAllowedList.end(),pSong) == m_vpSongAllowedList.end() ) return false; } if( m_iMaxStagesForSong != -1 && GAMESTATE->GetNumStagesMultiplierForSong(pSong) > m_iMaxStagesForSong ) return false; switch( m_Tutorial ) { DEFAULT_FAIL(m_Tutorial); case Tutorial_Yes: if( !pSong->IsTutorial() ) return false; break; case Tutorial_No: if( pSong->IsTutorial() ) return false; break; case Tutorial_DontCare: break; } switch( m_Locked ) { DEFAULT_FAIL(m_Locked); case Locked_Locked: if( UNLOCKMAN && !(UNLOCKMAN->SongIsLocked(pSong) & LOCKED_LOCK) ) return false; break; case Locked_Unlocked: if( UNLOCKMAN && UNLOCKMAN->SongIsLocked(pSong) & LOCKED_LOCK ) return false; break; case Locked_DontCare: break; } return true; }
static NoteData ParseNoteData(RString &step1, RString &step2, Steps &out, const RString &path) { g_mapDanceNoteToNoteDataColumn.clear(); switch( out.m_StepsType ) { case StepsType_dance_single: g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3; break; case StepsType_dance_double: case StepsType_dance_couple: g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_LEFT] = 4; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_DOWN] = 5; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_UP] = 6; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_RIGHT] = 7; break; case StepsType_dance_solo: g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPLEFT] = 1; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 2; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 3; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPRIGHT] = 4; g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 5; break; DEFAULT_FAIL( out.m_StepsType ); } NoteData newNoteData; newNoteData.SetNumTracks( g_mapDanceNoteToNoteDataColumn.size() ); for( int pad=0; pad<2; pad++ ) // foreach pad { RString sStepData; switch( pad ) { case 0: sStepData = step1; break; case 1: if( step2 == "" ) // no data continue; // skip sStepData = step2; break; DEFAULT_FAIL( pad ); } sStepData.Replace("\n", ""); sStepData.Replace("\r", ""); sStepData.Replace("\t", ""); sStepData.Replace(" ", ""); double fCurrentBeat = 0; double fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE; for( size_t i=0; i<sStepData.size(); ) { char c = sStepData[i++]; switch( c ) { // begins a series case '(': fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE; break; case '[': fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE; break; case '{': fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE; break; case '`': fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE; break; // ends a series case ')': case ']': case '}': case '\'': case '>': fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE; break; default: // this is a note character { if( c == '!' ) { LOG->UserLog( "Song file", path, "has an unexpected character: '!'." ); continue; } bool jump = false; if( c == '<' ) { /* Arr. Is this a jump or a 1/192 marker? */ if( Is192( sStepData, i ) ) { fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE; break; } /* It's a jump. * We need to keep reading notes until we hit a >. */ jump = true; i++; } const int iIndex = BeatToNoteRow( (float)fCurrentBeat ); i--; do { c = sStepData[i++]; if( jump && c == '>' ) break; int iCol1, iCol2; DWIcharToNoteCol( c, (GameController)pad, iCol1, iCol2, path ); if( iCol1 != -1 ) newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_TAP); if( iCol2 != -1 ) newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_TAP); if(i>=sStepData.length()) { break; //we ran out of data //while looking for the ending > mark } if( sStepData[i] == '!' ) { i++; const char holdChar = sStepData[i++]; DWIcharToNoteCol(holdChar, (GameController)pad, iCol1, iCol2, path ); if( iCol1 != -1 ) newNoteData.SetTapNote(iCol1, iIndex, TAP_ORIGINAL_HOLD_HEAD); if( iCol2 != -1 ) newNoteData.SetTapNote(iCol2, iIndex, TAP_ORIGINAL_HOLD_HEAD); } } while( jump ); fCurrentBeat += fCurrentIncrementer; } break; } } } /* Fill in iDuration. */ for( int t=0; t<newNoteData.GetNumTracks(); ++t ) { FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow ) { TapNote tn = newNoteData.GetTapNote( t, iHeadRow ); if( tn.type != TapNote::hold_head ) continue; int iTailRow = iHeadRow; bool bFound = false; while( !bFound && newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) ) { const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow ); if( TailTap.type == TapNote::empty ) continue; newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY ); tn.iDuration = iTailRow - iHeadRow; newNoteData.SetTapNote( t, iHeadRow, tn ); bFound = true; } if( !bFound ) { /* The hold was never closed. */ LOG->UserLog("Song file", path, "failed to close a hold note in \"%s\" on track %i", DifficultyToString(out.GetDifficulty()).c_str(), t); newNoteData.SetTapNote( t, iHeadRow, TAP_EMPTY ); } } }
void NoteDataWithScoring::GetActualRadarValues(const NoteData &in, const PlayerStageStats &pss, float song_seconds, RadarValues& out) { // Anybody editing this function should also examine // NoteDataUtil::CalculateRadarValues to make sure it handles things the // same way. // Some of this logic is similar or identical to // NoteDataUtil::CalculateRadarValues because I couldn't figure out a good // way to combine them into one. -Kyz PlayerNumber pn= pss.m_player_number; garv_state state; NoteData::all_tracks_const_iterator curr_note= in.GetTapNoteRangeAllTracks(0, MAX_NOTE_ROW); TimingData* timing= GAMESTATE->GetProcessedTimingData(); // first_hittable_row and last_hittable_row exist so that // GetActualVoltageRadarValue can be passed the correct song length. // GetActualVoltageRadarValue scores based on the max combo, a full combo // is a full voltage score. The song length is used instead of trying to // figure out the max combo for the song because rolls mean there isn't a // limit to the max combo. -Kyz int first_hittable_row= -1; int last_hittable_row= -1; bool tick_holds= GAMESTATE->GetCurrentGame()->m_bTickHolds; while(!curr_note.IsAtEnd()) { if(curr_note.Row() != state.curr_row) { DoRowEndRadarActualCalc(state, out); state.curr_row= curr_note.Row(); state.num_notes_on_curr_row= 0; state.num_holds_on_curr_row= 0; state.judgable= timing->IsJudgableAtRow(state.curr_row); for(size_t n= 0; n < state.hold_ends.size(); ++n) { if(state.hold_ends[n].end_row < state.curr_row) { state.hold_ends.erase(state.hold_ends.begin() + n); --n; } } state.last_tns_on_row= TapNoteScore_Invalid; state.last_time_on_row= -9999; state.worst_tns_on_row= TapNoteScore_Invalid; } bool for_this_player= curr_note->pn == pn || pn == PLAYER_INVALID || curr_note->pn == PLAYER_INVALID; if(state.judgable && for_this_player) { switch(curr_note->type) { case TapNoteType_HoldTail: // If there are tick holds, then the hold tail needs to be counted // in last_hittable_row because that's where the combo will end. // -Kyz if(tick_holds) { UpdateHittable(state.curr_row, first_hittable_row, last_hittable_row); } break; case TapNoteType_Tap: case TapNoteType_HoldHead: // HoldTails and Attacks are counted by IsTap. But it doesn't // make sense to count HoldTails as hittable notes. -Kyz case TapNoteType_Attack: case TapNoteType_Lift: UpdateHittable(state.curr_row, first_hittable_row, last_hittable_row); ++state.num_notes_on_curr_row; state.notes_hit_for_stream+= (curr_note->result.tns >= state.stream_tns); state.notes_hit+= (curr_note->result.tns >= state.taps_tns); if(curr_note->result.tns < state.worst_tns_on_row) { state.worst_tns_on_row= curr_note->result.tns; } if(curr_note->result.fTapNoteOffset > state.last_time_on_row) { state.last_time_on_row= curr_note->result.fTapNoteOffset; state.last_tns_on_row= curr_note->result.tns; } if(curr_note->type == TapNoteType_HoldHead) { if(curr_note->subType == TapNoteSubType_Hold) { state.holds_held+= (curr_note->HoldResult.hns == HNS_Held); } else if(curr_note->subType == TapNoteSubType_Roll) { state.rolls_held+= (curr_note->HoldResult.hns == HNS_Held); } state.hold_ends.push_back( hold_status(state.curr_row + curr_note->iDuration, curr_note->HoldResult.iLastHeldRow)); ++state.num_holds_on_curr_row; } else if(curr_note->type == TapNoteType_Lift) { state.lifts_hit+= (curr_note->result.tns >= state.lifts_tns); } break; case TapNoteType_Mine: state.mines_avoided+= (curr_note->result.tns == TNS_AvoidMine); break; case TapNoteType_Fake: default: break; } } ++curr_note; } DoRowEndRadarActualCalc(state, out); // ScreenGameplay passes in the RadarValues that were calculated by // NoteDataUtil::CalculateRadarValues, so those are reused here. -Kyz int note_count= out[RadarCategory_Notes]; int jump_count= out[RadarCategory_Jumps]; int hold_count= out[RadarCategory_Holds]; int tap_count= out[RadarCategory_TapsAndHolds]; float hittable_steps_length= max(0, timing->GetElapsedTimeFromBeat(NoteRowToBeat(last_hittable_row)) - timing->GetElapsedTimeFromBeat(NoteRowToBeat(first_hittable_row))); // The for loop and the assert are used to ensure that all fields of // RadarValue get set in here. FOREACH_ENUM(RadarCategory, rc) { switch(rc) { case RadarCategory_Stream: out[rc]= clamp(float(state.notes_hit_for_stream) / note_count, 0.0f, 1.0f); break; case RadarCategory_Voltage: out[rc]= GetActualVoltageRadarValue(in, hittable_steps_length, pss); break; case RadarCategory_Air: out[rc]= clamp(float(state.jumps_hit_for_air) / jump_count, 0.0f, 1.0f); break; case RadarCategory_Freeze: out[rc]= clamp(float(state.holds_held) / hold_count, 0.0f, 1.0f); break; case RadarCategory_Chaos: out[rc]= GetActualChaosRadarValue(in, song_seconds, pss); break; case RadarCategory_TapsAndHolds: out[rc]= state.taps_hit; break; case RadarCategory_Jumps: out[rc]= state.jumps_hit; break; case RadarCategory_Holds: out[rc]= state.holds_held; break; case RadarCategory_Mines: out[rc]= state.mines_avoided; break; case RadarCategory_Hands: out[rc]= state.hands_hit; break; case RadarCategory_Rolls: out[rc]= state.rolls_held; break; case RadarCategory_Lifts: out[rc]= state.lifts_hit; break; case RadarCategory_Fakes: out[rc]= out[rc]; break; case RadarCategory_Notes: out[rc]= state.notes_hit; break; DEFAULT_FAIL(rc); } } }