Track held misses
This adds a `HeldMiss` attribute to judgment messages and a GetHeld() method to TapNoteResult.
This commit is contained in:
@@ -2027,6 +2027,7 @@
|
|||||||
<Function name='GetTapNoteScore'/>
|
<Function name='GetTapNoteScore'/>
|
||||||
<Function name='GetTapNoteOffset'/>
|
<Function name='GetTapNoteOffset'/>
|
||||||
<Function name='GetHidden'/>
|
<Function name='GetHidden'/>
|
||||||
|
<Function name='GetHeld'/>
|
||||||
</Class>
|
</Class>
|
||||||
<Class name='HoldNoteResult'>
|
<Class name='HoldNoteResult'>
|
||||||
<Function name='GetHoldNoteScore'/>
|
<Function name='GetHoldNoteScore'/>
|
||||||
|
|||||||
@@ -6098,6 +6098,11 @@ local spr = Def.Sprite{
|
|||||||
<Function name='GetHidden' return='bool' arguments=''>
|
<Function name='GetHidden' return='bool' arguments=''>
|
||||||
Returns true if the Tap Note was judged with a result that would stop it from drawing.
|
Returns true if the Tap Note was judged with a result that would stop it from drawing.
|
||||||
</Function>
|
</Function>
|
||||||
|
<Function name='GetHeld' return='bool' arguments=''>
|
||||||
|
Returns whether the input for the Tap Note was ever held during
|
||||||
|
the judgment interval. Useful to distinguish a normal miss from
|
||||||
|
a held miss.
|
||||||
|
</Function>
|
||||||
</Class>
|
</Class>
|
||||||
<Class name='HoldNoteResult'>
|
<Class name='HoldNoteResult'>
|
||||||
<Function name='GetHoldNoteScore' return='HoldNoteScore' arguments=''>
|
<Function name='GetHoldNoteScore' return='HoldNoteScore' arguments=''>
|
||||||
|
|||||||
@@ -210,12 +210,14 @@ public:
|
|||||||
DEFINE_METHOD(GetTapNoteScore, tns);
|
DEFINE_METHOD(GetTapNoteScore, tns);
|
||||||
DEFINE_METHOD(GetTapNoteOffset, fTapNoteOffset);
|
DEFINE_METHOD(GetTapNoteOffset, fTapNoteOffset);
|
||||||
DEFINE_METHOD(GetHidden, bHidden);
|
DEFINE_METHOD(GetHidden, bHidden);
|
||||||
|
DEFINE_METHOD(GetHeld, bHeld);
|
||||||
|
|
||||||
LunaTapNoteResult()
|
LunaTapNoteResult()
|
||||||
{
|
{
|
||||||
ADD_METHOD( GetTapNoteScore );
|
ADD_METHOD( GetTapNoteScore );
|
||||||
ADD_METHOD( GetTapNoteOffset );
|
ADD_METHOD( GetTapNoteOffset );
|
||||||
ADD_METHOD( GetHidden );
|
ADD_METHOD( GetHidden );
|
||||||
|
ADD_METHOD( GetHeld );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
LUA_REGISTER_CLASS( TapNoteResult )
|
LUA_REGISTER_CLASS( TapNoteResult )
|
||||||
|
|||||||
+4
-1
@@ -13,7 +13,7 @@ class XNode;
|
|||||||
struct TapNoteResult
|
struct TapNoteResult
|
||||||
{
|
{
|
||||||
/** @brief Set up the TapNoteResult with default values. */
|
/** @brief Set up the TapNoteResult with default values. */
|
||||||
TapNoteResult() : tns(TNS_None), fTapNoteOffset(0.f), bHidden(false) { }
|
TapNoteResult() : tns(TNS_None), fTapNoteOffset(0.f), bHidden(false), bHeld(false) { }
|
||||||
/** @brief The TapNoteScore that was achieved by the player. */
|
/** @brief The TapNoteScore that was achieved by the player. */
|
||||||
TapNoteScore tns;
|
TapNoteScore tns;
|
||||||
|
|
||||||
@@ -28,6 +28,9 @@ struct TapNoteResult
|
|||||||
/** @brief If the whole row has been judged, all taps on the row will be set to hidden. */
|
/** @brief If the whole row has been judged, all taps on the row will be set to hidden. */
|
||||||
bool bHidden;
|
bool bHidden;
|
||||||
|
|
||||||
|
/** @brief Track if the note was held. Used to track held misses. */
|
||||||
|
bool bHeld;
|
||||||
|
|
||||||
// XML
|
// XML
|
||||||
XNode* CreateNode() const;
|
XNode* CreateNode() const;
|
||||||
void LoadFromNode( const XNode* pNode );
|
void LoadFromNode( const XNode* pNode );
|
||||||
|
|||||||
@@ -993,6 +993,72 @@ void Player::Update( float fDeltaTime )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track held misses
|
||||||
|
//
|
||||||
|
// In order to track held misses we have to check whether a note was
|
||||||
|
// held any time during the judgment window before it is judged a miss.
|
||||||
|
// Note: at this point we don't actually know yet whether a note will
|
||||||
|
// be a miss or a hit, so we have to track for all notes whether they
|
||||||
|
// were held at some point before getting judged.
|
||||||
|
{
|
||||||
|
float largestWindow = 0.0f;
|
||||||
|
largestWindow = max(largestWindow, GetWindowSeconds(TW_W1));
|
||||||
|
largestWindow = max(largestWindow, GetWindowSeconds(TW_W2));
|
||||||
|
largestWindow = max(largestWindow, GetWindowSeconds(TW_W3));
|
||||||
|
largestWindow = max(largestWindow, GetWindowSeconds(TW_W4));
|
||||||
|
largestWindow = max(largestWindow, GetWindowSeconds(TW_W5));
|
||||||
|
|
||||||
|
// We have to check the unjudged notes that are within the
|
||||||
|
// timing window. Let's find the cutoff point! (lastCheckRow)
|
||||||
|
const float rate = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
||||||
|
const SongPosition songPosition = m_pPlayerState->m_Position;
|
||||||
|
const float musicPosition = songPosition.m_fMusicSeconds + (songPosition.m_LastBeatUpdate.Ago() * rate);
|
||||||
|
// We have to add 1 here, because GetBeatFromElapsedTime() can round down.
|
||||||
|
const int lastCheckRow = BeatToNoteRow(m_Timing->GetBeatFromElapsedTime(musicPosition + (largestWindow * rate)) + 1);
|
||||||
|
|
||||||
|
// The button being held only counts for the first unjudged
|
||||||
|
// note on a track (== column/arrow direction), so we have to
|
||||||
|
// keep track for which tracks we have already seen an unjudged
|
||||||
|
// note.
|
||||||
|
vector<bool> seenTracks(m_NoteData.GetNumTracks(), false);
|
||||||
|
|
||||||
|
for(auto iter = *m_pIterNeedsTapJudging; !iter.IsAtEnd() && iter.Row() <= lastCheckRow; ++iter)
|
||||||
|
{
|
||||||
|
TapNote &tn = *iter;
|
||||||
|
const int row = iter.Row();
|
||||||
|
const int track = iter.Track();
|
||||||
|
|
||||||
|
// Skip over warp and fake segments
|
||||||
|
if (!m_Timing->IsJudgableAtRow(row))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Held misses only apply to tap notes
|
||||||
|
if (tn.type != TapNoteType_Tap && tn.type != TapNoteType_HoldHead)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const float notePosition = m_Timing->GetElapsedTimeFromBeat(NoteRowToBeat(row));
|
||||||
|
const float offset = fabsf((notePosition - musicPosition) / rate);
|
||||||
|
|
||||||
|
// Skip if we are outside of the largest timing window
|
||||||
|
if (offset > largestWindow)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Skip the note if there is an earlier note on the same track that still awaits judgement
|
||||||
|
if (seenTracks[track])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
seenTracks[track] = true;
|
||||||
|
|
||||||
|
if (!tn.result.bHeld)
|
||||||
|
{
|
||||||
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
||||||
|
vector<GameInput> input;
|
||||||
|
GAMESTATE->GetCurrentStyle(pn)->StyleInputToGameInput(track, pn, input);
|
||||||
|
|
||||||
|
tn.result.bHeld = INPUTMAPPER->IsBeingPressed(input, m_pPlayerState->m_mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update HoldNotes logic
|
// update HoldNotes logic
|
||||||
{
|
{
|
||||||
@@ -3120,6 +3186,9 @@ void Player::SetJudgment( int iRow, int iTrack, const TapNote &tn, TapNoteScore
|
|||||||
msg.SetParam( "Early", fTapNoteOffset < 0.0f );
|
msg.SetParam( "Early", fTapNoteOffset < 0.0f );
|
||||||
msg.SetParam( "TapNoteOffset", tn.result.fTapNoteOffset );
|
msg.SetParam( "TapNoteOffset", tn.result.fTapNoteOffset );
|
||||||
|
|
||||||
|
if ( tns == TNS_Miss )
|
||||||
|
msg.SetParam( "HeldMiss", tn.result.bHeld );
|
||||||
|
|
||||||
Lua* L= LUA->Get();
|
Lua* L= LUA->Get();
|
||||||
lua_createtable( L, 0, m_NoteData.GetNumTracks() ); // TapNotes this row
|
lua_createtable( L, 0, m_NoteData.GetNumTracks() ); // TapNotes this row
|
||||||
lua_createtable( L, 0, m_NoteData.GetNumTracks() ); // HoldHeads of tracks held at this row.
|
lua_createtable( L, 0, m_NoteData.GetNumTracks() ); // HoldHeads of tracks held at this row.
|
||||||
|
|||||||
Reference in New Issue
Block a user