Add TimingAssist; this roughly implements the timing helper in PNM.

This lets us add an announcer that's played after each non-perfect
step, eg. "gameplay assist late good".

It only works reasonably for sparse songs--songs that only have notes
far enough apart that the announcer makes sense.  That's mostly
beginner songs, which is what this is targetted at anyway.  It's meaningless
to use this assist mode in harder songs; even if the announcer was
rate limited, it'd be impossible to tell which step the announcer was for,
so I didn't bother trying.  (It'd be amusing to turn this on in MaxU. :)

A different timing assist type (TimingTip) is probably coming soon,
targetted to higher level (light/med) songs.

The only major problem at the moment is that there's no way to tell
who the announcer is talking to if there's two players.  If we implement
per-player announcers, that would help (and allow a number of other
neat things, like having the p1 announcer say "Ready" and the p2
announcer say "HWG").
This commit is contained in:
Glenn Maynard
2003-03-27 00:48:58 +00:00
parent 3f0fdda811
commit 1c817ca08d
2 changed files with 137 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
#include "global.h"
#include "TimingAssist.h"
#include "AnnouncerManager.h"
#include "GameState.h"
#include "NoteData.h"
#include "NoteTypes.h"
#include "PrefsManager.h"
#include "RageLog.h"
TimingAssist::TimingAssist()
{
for(int i = 0; i < 2; ++i)
{
const CString direction = i? "early":"late";
for(TapNoteScore j = TNS_BOO; j < TNS_MARVELOUS; j = TapNoteScore(j+1))
{
CString name;
switch(j)
{
case TNS_MISS: name = "miss"; break;
case TNS_BOO: name = "boo"; break;
case TNS_GOOD: name = "good"; break;
case TNS_GREAT: name = "great"; break;
case TNS_PERFECT: name = "perfect"; break;
}
CString path = ssprintf("gameplay assist %s %s", direction.GetString(), name.GetString());
Timing[j][i].Load(ANNOUNCER->GetPathTo(path));
}
}
Miss.Load(ANNOUNCER->GetPathTo("gameplay assist miss"));
memset(data, 0, sizeof(data));
}
void TimingAssist::Load(PlayerNumber pn, NoteDataWithScoring *nd)
{
data[pn] = nd;
}
void TimingAssist::Reset()
{
LastRow = 0;
}
void TimingAssist::Update(float fDeltaTime)
{
Actor::Update(fDeltaTime);
for( int pn=0; pn<NUM_PLAYERS; pn++ )
{
if( !GAMESTATE->IsPlayerEnabled(PlayerNumber(pn)) )
continue;
DoTimingAssist((PlayerNumber)pn);
}
}
void TimingAssist::DoTimingAssist(PlayerNumber pn)
{
ASSERT(data[pn]);
if(!GAMESTATE->m_PlayerOptions[pn].m_bTimingAssist)
return;
const int maxrow = BeatToNoteRow(GAMESTATE->m_fSongBeat);
for( ; LastRow <= maxrow; LastRow++)
{
TapNoteScore mintns = data[pn]->MinTapNoteScore(LastRow);
/* If the minimum score is TNS_NONE, then not all notes on this row have
* been graded, so stop. */
if(mintns == TNS_NONE) return;
/* Don't announce if the score is the maximum. */
if(mintns == TNS_MARVELOUS) continue;
if(mintns == TNS_PERFECT && !PREFSMAN->m_bMarvelousTiming) continue;
if(mintns == TNS_MISS)
{
Miss.PlayRandom();
continue;
}
const int last_tns_track = data[pn]->LastTapNoteScoreTrack(LastRow);
/* We shouldn't get here if there're no notes on this row, since MinTNS
* above should have returned TNS_NONE. */
ASSERT(last_tns_track != -1);
const TapNoteScore last_tns = data[pn]->GetTapNoteScore(last_tns_track, LastRow);
const float offset = data[pn]->GetTapNoteOffset(last_tns_track, LastRow);
const bool early = offset < 0;
Timing[last_tns][early].PlayRandom();
}
}
/*
-----------------------------------------------------------------------------
Copyright (c) 2003 by the person(s) listed below. All rights reserved.
Glenn Maynard
-----------------------------------------------------------------------------
*/