2011-03-17 01:47:30 -04:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "NoteDataWithScoring.h"
|
|
|
|
|
#include "NoteData.h"
|
|
|
|
|
#include "PlayerStageStats.h"
|
|
|
|
|
#include "GameConstantsAndTypes.h"
|
2015-04-06 18:45:23 -06:00
|
|
|
#include "GameState.h"
|
2011-03-17 01:47:30 -04:00
|
|
|
#include "ThemeMetric.h"
|
|
|
|
|
#include "RageLog.h"
|
2015-04-06 18:45:23 -06:00
|
|
|
#include "TimingData.h"
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
//ThemeMetric<TapNoteScoreJudgeType> LAST_OR_MINIMUM_TNS ("Gameplay","LastOrMinimumTapNoteScore");
|
2011-08-11 21:19:43 -04:00
|
|
|
static ThemeMetric<TapNoteScore> MIN_SCORE_TO_MAINTAIN_COMBO( "Gameplay", "MinScoreToMaintainCombo" );
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
/* Return the last tap score of a row: the grade of the tap that completed
|
|
|
|
|
* the row. If the row has no tap notes, return -1. If any tap notes aren't
|
|
|
|
|
* graded (any tap is TNS_None) or are missed (TNS_Miss), return it. */
|
|
|
|
|
int LastTapNoteScoreTrack( const NoteData &in, unsigned iRow, PlayerNumber pn )
|
|
|
|
|
{
|
|
|
|
|
float scoretime = -9999;
|
|
|
|
|
int best_track = -1;
|
|
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
/* Skip empty tracks and mines */
|
|
|
|
|
const TapNote &tn = in.GetTapNote( t, iRow );
|
2014-07-25 18:45:02 -05:00
|
|
|
if (tn.type == TapNoteType_Empty ||
|
|
|
|
|
tn.type == TapNoteType_Mine ||
|
|
|
|
|
tn.type == TapNoteType_Fake ||
|
|
|
|
|
tn.type == TapNoteType_AutoKeysound)
|
2011-03-17 01:47:30 -04:00
|
|
|
continue;
|
|
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != pn && pn != PLAYER_INVALID )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
TapNoteScore tns = tn.result.tns;
|
|
|
|
|
|
|
|
|
|
if( tns == TNS_Miss || tns == TNS_None )
|
|
|
|
|
return t;
|
|
|
|
|
|
|
|
|
|
float tm = tn.result.fTapNoteOffset;
|
|
|
|
|
if(tm < scoretime) continue;
|
|
|
|
|
|
|
|
|
|
scoretime = tm;
|
|
|
|
|
best_track = t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return best_track;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the minimum tap score of a row: the lowest grade of the tap in the row.
|
|
|
|
|
* If the row isn't complete (not all taps have been hit),
|
|
|
|
|
* return TNS_NONE or TNS_MISS. */
|
|
|
|
|
#if 0
|
|
|
|
|
int MinTapNoteScoreTrack( const NoteData &in, unsigned iRow, PlayerNumber pn )
|
|
|
|
|
{
|
|
|
|
|
// work in progress
|
|
|
|
|
float scoretime = -9999;
|
|
|
|
|
int worst_track = -1;
|
|
|
|
|
TapNoteScore lowestTNS = TapNoteScore_Invalid;
|
|
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
// Skip empty tracks and mines
|
|
|
|
|
const TapNote &tn = in.GetTapNote( t, iRow );
|
2014-07-25 18:45:02 -05:00
|
|
|
if (tn.type == TapNoteType_Empty ||
|
|
|
|
|
tn.type == TapNoteType_Mine ||
|
|
|
|
|
tn.type == TapNoteType_Fake ||
|
|
|
|
|
tn.type == TapNoteType_AutoKeysound)
|
2011-03-17 01:47:30 -04:00
|
|
|
continue;
|
|
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != pn && pn != PLAYER_INVALID )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
TapNoteScore tns = tn.result.tns;
|
|
|
|
|
|
|
|
|
|
if( tns == TNS_Miss || tns == TNS_None )
|
|
|
|
|
return t;
|
|
|
|
|
|
|
|
|
|
float tm = tn.result.fTapNoteOffset;
|
|
|
|
|
if(tm > scoretime) continue; // huh -aj
|
|
|
|
|
|
|
|
|
|
// enum compare against lowestTNS here
|
|
|
|
|
//if( tns < lowestTNS ) continue;
|
|
|
|
|
|
|
|
|
|
scoretime = tm;
|
|
|
|
|
worst_track = t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return worst_track;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-12 08:35:08 -06:00
|
|
|
const TapNote &NoteDataWithScoring::LastTapNoteWithResult( const NoteData &in, unsigned iRow, PlayerNumber plnum )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
// Allow this to be configurable between LastTapNoteScoreTrack and
|
|
|
|
|
// MinTapNoteScore; this change inspired by PumpMania (Zmey, et al) -aj
|
|
|
|
|
/*
|
|
|
|
|
LOG->Trace( ssprintf("hi i'm NoteDataWithScoring::LastTapNoteWithResult(NoteData in, iRow=%i, PlayerNumber pn)", iRow) );
|
|
|
|
|
int iTrack = 0;
|
|
|
|
|
switch(LAST_OR_MINIMUM_TNS)
|
|
|
|
|
{
|
|
|
|
|
case TapNoteScoreJudgeType_MinimumScore:
|
|
|
|
|
iTrack = MinTapNoteScoreTrack( in, iRow, pn );
|
|
|
|
|
LOG->Trace( ssprintf("TapNoteScoreJudgeType_MinimumScore omg iTrack is %i and iRow is %i",iTrack,iRow) );
|
|
|
|
|
break;
|
|
|
|
|
case TapNoteScoreJudgeType_LastScore:
|
|
|
|
|
default:
|
|
|
|
|
iTrack = LastTapNoteScoreTrack( in, iRow, pn );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
*/
|
2014-01-12 08:35:08 -06:00
|
|
|
int iTrack = LastTapNoteScoreTrack( in, iRow, plnum );
|
2011-03-17 01:47:30 -04:00
|
|
|
if( iTrack == -1 )
|
|
|
|
|
return TAP_EMPTY;
|
|
|
|
|
|
|
|
|
|
//LOG->Trace( ssprintf("returning in.GetTapNote(iTrack=%i, iRow=%i)", iTrack, iRow) );
|
|
|
|
|
return in.GetTapNote( iTrack, iRow );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the minimum tap score of a row. If the row isn't complete (not all
|
|
|
|
|
* taps have been hit), return TNS_None or TNS_Miss. */
|
2014-01-12 08:35:08 -06:00
|
|
|
TapNoteScore NoteDataWithScoring::MinTapNoteScore( const NoteData &in, unsigned row, PlayerNumber plnum )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
//LOG->Trace("Hey I'm NoteDataWithScoring::MinTapNoteScore");
|
|
|
|
|
TapNoteScore score = TNS_W1;
|
|
|
|
|
for( int t=0; t<in.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
// Ignore mines (and fake arrows), or the score will always be TNS_None.
|
|
|
|
|
const TapNote &tn = in.GetTapNote( t, row );
|
2014-07-25 18:45:02 -05:00
|
|
|
if (tn.type == TapNoteType_Empty ||
|
|
|
|
|
tn.type == TapNoteType_Mine ||
|
|
|
|
|
tn.type == TapNoteType_Fake ||
|
|
|
|
|
tn.type == TapNoteType_AutoKeysound ||
|
2014-01-12 08:35:08 -06:00
|
|
|
( plnum != PlayerNumber_Invalid && tn.pn != plnum ) )
|
2011-03-17 01:47:30 -04:00
|
|
|
continue;
|
|
|
|
|
score = min( score, tn.result.tns );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//LOG->Trace( ssprintf("OMG score is?? %s",TapNoteScoreToString(score).c_str()) );
|
|
|
|
|
return score;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-12 08:35:08 -06:00
|
|
|
bool NoteDataWithScoring::IsRowCompletelyJudged( const NoteData &in, unsigned row, PlayerNumber plnum )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2014-01-12 08:35:08 -06:00
|
|
|
return MinTapNoteScore( in, row, plnum ) >= TNS_Miss;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
// Return the ratio of actual combo to max combo.
|
|
|
|
|
float GetActualVoltageRadarValue( const NoteData &in, float fSongSeconds, const PlayerStageStats &pss )
|
|
|
|
|
{
|
|
|
|
|
/* STATSMAN->m_CurStageStats.iMaxCombo is unrelated to GetNumTapNotes:
|
|
|
|
|
* m_bComboContinuesBetweenSongs might be on, and the way combo is counted
|
|
|
|
|
* varies depending on the mode and score keeper. Instead, let's use the
|
|
|
|
|
* length of the longest recorded combo. This is only subtly different:
|
|
|
|
|
* it's the percent of the song the longest combo took to get. */
|
2015-04-06 18:45:23 -06:00
|
|
|
// FIXME:
|
|
|
|
|
// If MaxCombo.m_fSizeSeconds is used, it is wrong (too short) for any song
|
|
|
|
|
// where the last second is after the last step. However, calculating the
|
|
|
|
|
// max combo possible would require consulting the timing data on every step
|
|
|
|
|
// for combo segments and generally be a painful waste of effort. -Kyz
|
2011-03-17 01:47:30 -04:00
|
|
|
const PlayerStageStats::Combo_t MaxCombo = pss.GetMaxCombo();
|
|
|
|
|
float fComboPercent = SCALE( MaxCombo.m_fSizeSeconds, 0, pss.m_fLastSecond-pss.m_fFirstSecond, 0.0f, 1.0f );
|
|
|
|
|
return clamp( fComboPercent, 0.0f, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the ratio of actual to possible dance points.
|
|
|
|
|
float GetActualChaosRadarValue( const NoteData &in, float fSongSeconds, const PlayerStageStats &pss )
|
|
|
|
|
{
|
|
|
|
|
const int iPossibleDP = pss.m_iPossibleDancePoints;
|
|
|
|
|
if ( iPossibleDP == 0 )
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
const int ActualDP = pss.m_iActualDancePoints;
|
|
|
|
|
return clamp( float(ActualDP)/iPossibleDP, 0.0f, 1.0f );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-06 21:49:46 -06:00
|
|
|
struct hold_status
|
2015-04-06 18:45:23 -06:00
|
|
|
{
|
|
|
|
|
int end_row;
|
|
|
|
|
int last_held_row;
|
2015-04-06 21:49:46 -06:00
|
|
|
hold_status(int e, int l)
|
2015-04-06 18:45:23 -06:00
|
|
|
:end_row(e), last_held_row(l)
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
|
2015-04-06 21:49:46 -06:00
|
|
|
struct garv_state
|
2015-04-06 18:45:23 -06:00
|
|
|
{
|
|
|
|
|
int curr_row;
|
|
|
|
|
int notes_hit_for_stream;
|
|
|
|
|
int jumps_hit_for_air;
|
|
|
|
|
int holds_held;
|
|
|
|
|
int rolls_held;
|
|
|
|
|
int notes_hit;
|
|
|
|
|
int taps_hit;
|
|
|
|
|
int jumps_hit;
|
|
|
|
|
int hands_hit;
|
|
|
|
|
int mines_avoided;
|
|
|
|
|
int lifts_hit;
|
|
|
|
|
// hold_ends tracks where currently active holds will end, which is used
|
|
|
|
|
// to count the number of hands. -Kyz
|
2015-04-06 21:49:46 -06:00
|
|
|
vector<hold_status> hold_ends;
|
2015-04-06 18:45:23 -06:00
|
|
|
int num_notes_on_curr_row;
|
|
|
|
|
// num_holds_on_curr_row saves us the work of tracking where holds started
|
|
|
|
|
// just to keep a jump of two holds from counting as a hand.
|
|
|
|
|
int num_holds_on_curr_row;
|
|
|
|
|
int num_notes_hit_on_curr_row;
|
|
|
|
|
// last_tns_on_row and last_time_on_row are used for deciding whether a jump
|
|
|
|
|
// or hand was successfully hit.
|
|
|
|
|
TapNoteScore last_tns_on_row;
|
|
|
|
|
float last_time_on_row;
|
|
|
|
|
// A hand is considered missed if any of the notes is missed.
|
|
|
|
|
TapNoteScore worst_tns_on_row;
|
|
|
|
|
// TODO? Make these configurable in some way?
|
|
|
|
|
TapNoteScore stream_tns;
|
|
|
|
|
TapNoteScore air_tns;
|
|
|
|
|
TapNoteScore taps_tns;
|
|
|
|
|
TapNoteScore jumps_tns;
|
|
|
|
|
TapNoteScore hands_tns;
|
|
|
|
|
TapNoteScore lifts_tns;
|
|
|
|
|
bool judgable;
|
2015-04-06 21:49:46 -06:00
|
|
|
garv_state()
|
2015-04-06 18:45:23 -06:00
|
|
|
:curr_row(0), notes_hit_for_stream(0), jumps_hit_for_air(0),
|
|
|
|
|
holds_held(0), rolls_held(0), notes_hit(0), taps_hit(0), jumps_hit(0),
|
|
|
|
|
hands_hit(0), mines_avoided(0), lifts_hit(0), num_notes_on_curr_row(0),
|
|
|
|
|
num_holds_on_curr_row(0), num_notes_hit_on_curr_row(0),
|
|
|
|
|
last_tns_on_row(TapNoteScore_Invalid), last_time_on_row(-9999),
|
|
|
|
|
worst_tns_on_row(TapNoteScore_Invalid), stream_tns(TNS_W2),
|
|
|
|
|
air_tns(TNS_W2), taps_tns(TNS_W4), jumps_tns(TNS_W4), hands_tns(TNS_W4),
|
|
|
|
|
lifts_tns(MIN_SCORE_TO_MAINTAIN_COMBO),
|
|
|
|
|
judgable(false)
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
|
2015-04-06 21:49:46 -06:00
|
|
|
static void DoRowEndRadarActualCalc(garv_state& state, RadarValues& out)
|
2015-04-06 18:45:23 -06:00
|
|
|
{
|
|
|
|
|
if(state.judgable && state.last_tns_on_row != TapNoteScore_Invalid)
|
|
|
|
|
{
|
|
|
|
|
if(state.num_notes_on_curr_row >= 1)
|
|
|
|
|
{
|
|
|
|
|
state.taps_hit+= (state.last_tns_on_row >= state.taps_tns);
|
|
|
|
|
}
|
|
|
|
|
if(state.num_notes_on_curr_row >= 2)
|
|
|
|
|
{
|
|
|
|
|
state.jumps_hit_for_air+= (state.last_tns_on_row >= state.air_tns);
|
|
|
|
|
state.jumps_hit+= (state.last_tns_on_row >= state.jumps_tns);
|
|
|
|
|
}
|
|
|
|
|
if(state.num_notes_on_curr_row + (state.hold_ends.size() -
|
|
|
|
|
state.num_holds_on_curr_row) >= 3)
|
|
|
|
|
{
|
|
|
|
|
if(state.worst_tns_on_row >= state.hands_tns)
|
|
|
|
|
{
|
|
|
|
|
size_t holds_down= 0;
|
|
|
|
|
for(size_t n= 0; n < state.hold_ends.size(); ++n)
|
|
|
|
|
{
|
|
|
|
|
holds_down+= (state.curr_row <= state.hold_ends[n].last_held_row);
|
|
|
|
|
}
|
|
|
|
|
state.hands_hit+= (holds_down == state.hold_ends.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
2015-04-06 18:45:23 -06:00
|
|
|
void NoteDataWithScoring::GetActualRadarValues(const NoteData &in,
|
|
|
|
|
const PlayerStageStats &pss, float song_seconds, RadarValues& out)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-04-06 18:45:23 -06:00
|
|
|
// 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;
|
2015-04-06 21:49:46 -06:00
|
|
|
garv_state state;
|
2015-04-06 18:45:23 -06:00
|
|
|
|
|
|
|
|
NoteData::all_tracks_const_iterator curr_note=
|
|
|
|
|
in.GetTapNoteRangeAllTracks(0, MAX_NOTE_ROW);
|
|
|
|
|
TimingData* timing= GAMESTATE->GetProcessedTimingData();
|
|
|
|
|
|
|
|
|
|
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_Tap:
|
|
|
|
|
case TapNoteType_HoldHead:
|
|
|
|
|
// HoldTails and Attacks are counted by IsTap. -Kyz
|
|
|
|
|
case TapNoteType_HoldTail:
|
|
|
|
|
case TapNoteType_Attack:
|
|
|
|
|
case TapNoteType_Lift:
|
|
|
|
|
++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(
|
2015-04-06 21:49:46 -06:00
|
|
|
hold_status(state.curr_row + curr_note->iDuration,
|
2015-04-06 18:45:23 -06:00
|
|
|
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:
|
|
|
|
|
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];
|
2011-03-17 01:47:30 -04:00
|
|
|
// The for loop and the assert are used to ensure that all fields of
|
|
|
|
|
// RadarValue get set in here.
|
2015-04-06 18:45:23 -06:00
|
|
|
FOREACH_ENUM(RadarCategory, rc)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-04-06 18:45:23 -06:00
|
|
|
switch(rc)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2015-04-06 18:45:23 -06:00
|
|
|
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, song_seconds, 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);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (c) 2001-2004 Chris Danford, 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.
|
|
|
|
|
*/
|