Skip to content

Commit

Permalink
Merge pull request #262 from poco0317/replay_viewer2
Browse files Browse the repository at this point in the history
Improve Replay Behavior
  • Loading branch information
nico-abram authored Aug 27, 2018
2 parents c444cbf + b41591e commit 0af2f74
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 68 deletions.
7 changes: 7 additions & 0 deletions src/NoteTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,13 @@ struct HoldReplayResult {
TapNoteSubType subType;
};

struct TapReplayResult {
int row;
int track; // column
float offset; // 0
TapNoteType type; //typically mines, holds, rolls, etc
};

extern TapNote TAP_EMPTY; // '0'
extern TapNote TAP_ORIGINAL_TAP; // '1'
extern TapNote TAP_ORIGINAL_HOLD_HEAD; // '2'
Expand Down
96 changes: 78 additions & 18 deletions src/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "NoteSkinManager.h"
#include "ThemeMetric.h"
#include "HoldJudgment.h"
#include "GamePreferences.h"

RString ATTACK_DISPLAY_X_NAME( size_t p, size_t both_sides );
void TimingWindowSecondsInit( size_t /*TimingWindow*/ i, RString &sNameOut, float &defaultValueOut );
Expand Down Expand Up @@ -864,7 +865,7 @@ void Player::Update( float fDeltaTime )
if( tn.HoldResult.fLife >= 0.5f )
continue;

Step( iTrack, iHeadRow, now, false, false );
Step( iTrack, iHeadRow, now, true, false ); // bHeld really doesnt make a difference for autoplay and replay
if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY)
{
STATSMAN->m_CurStageStats.m_bUsedAutoplay = true;
Expand Down Expand Up @@ -1049,6 +1050,41 @@ void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vector<TrackRowTap
return; // we don't need to update the logic for this group
}

if (GamePreferences::m_AutoPlay == PC_REPLAY)
{
FOREACH(TrackRowTapNote, vTN, trtn)
{
TapNote &tn = *trtn->pTN;

// check from now until the head of the hold to see if it should die
// possibly really bad, but we dont REALLY care that much about fps in replays, right?
bool holdDropped = false;
for (int yeet = vTN[0].iRow; yeet <= iSongRow && !holdDropped; yeet++)
{
if (PlayerAI::DetermineIfHoldDropped(yeet, trtn->iTrack))
{
holdDropped = true;
}
}

if (holdDropped) // it should be dead
{
tn.HoldResult.bHeld = false;
tn.HoldResult.bActive = false;
tn.HoldResult.fLife = 0.f;
tn.HoldResult.hns = HNS_LetGo;

// score the dead hold
if (COMBO_BREAK_ON_IMMEDIATE_HOLD_LET_GO)
IncrementMissCombo();
SetHoldJudgment(tn, iFirstTrackWithMaxEndRow, iSongRow);
HandleHoldScore(tn);
return;
}
}
}


//LOG->Trace("hold note doesn't already have result, let's check.");

//LOG->Trace( ssprintf("[C++] hold note score: %s",HoldNoteScoreToString(hns).c_str()) );
Expand Down Expand Up @@ -2140,25 +2176,31 @@ void Player::Step( int col, int row, const std::chrono::steady_clock::time_point
break;

case PC_REPLAY:

fNoteOffset = PlayerAI::GetTapNoteOffsetForReplay(pTN, iRowOfOverlappingNoteOrRow, col);

if (fNoteOffset == -2.f)
{
CHECKPOINT_M("mine hit");
score = TNS_HitMine;
}
else if (pTN->type == TapNoteType_Mine)

if (bHeld) //a hack to make Rolls not do weird things like count as 0ms marvs.
{
return;
score = TNS_None;
fNoteOffset = -1.f;
}
else
{
if ( pTN->IsNote() )
score = PlayerAI::GetTapNoteScoreForReplay(m_pPlayerState, fNoteOffset);
fNoteOffset = PlayerAI::GetTapNoteOffsetForReplay(pTN, iRowOfOverlappingNoteOrRow, col);
if (fNoteOffset == -2.f) // we hit a mine
{
score = TNS_HitMine;
}
else if (pTN->type == TapNoteType_Mine) // we are looking at a mine but missed it
{
return;
}
else // every other case
{
if (pTN->IsNote())
score = PlayerAI::GetTapNoteScoreForReplay(m_pPlayerState, fNoteOffset);
}
}


break;
default:
FAIL_M(ssprintf("Invalid player controller type: %i", m_pPlayerState->m_PlayerController));
Expand Down Expand Up @@ -2197,8 +2239,15 @@ void Player::Step( int col, int row, const std::chrono::steady_clock::time_point

if ( pTN->result.tns != TNS_None )
{
SetJudgment(iRowOfOverlappingNoteOrRow, col, *pTN);
HandleTapRowScore(iRowOfOverlappingNoteOrRow);
if (pTN->type == TapNoteType_HoldHead && m_pPlayerState->m_PlayerController == PC_REPLAY && bHeld)
{
// odd hack to make roll taps (Step() with bHeld true) not count as marvs
}
else
{
SetJudgment(iRowOfOverlappingNoteOrRow, col, *pTN);
HandleTapRowScore(iRowOfOverlappingNoteOrRow);
}
}
}
}
Expand Down Expand Up @@ -2243,11 +2292,22 @@ void Player::Step( int col, int row, const std::chrono::steady_clock::time_point
}
}
// XXX:

if( !bRelease )
{
if( m_pNoteField != nullptr )
{
m_pNoteField->Step( col, score );
{ // skip tapping in replay mode on misses to emulate... missing.
if (m_pPlayerState->m_PlayerController == PC_REPLAY)
{
if (score != TNS_Miss)
{
m_pNoteField->Step(col, score);
}
}
else
{
m_pNoteField->Step(col, score);
}
}
Message msg( "Step" );
msg.SetParam( "PlayerNumber", m_pPlayerState->m_PlayerNumber );
Expand Down
125 changes: 81 additions & 44 deletions src/PlayerAI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ struct TapScoreDistribution
static TapScoreDistribution g_Distributions[NUM_SKILL_LEVELS];

HighScore* PlayerAI::pScoreData = nullptr;
map<int, vector<TapReplayResult>> PlayerAI::m_ReplayTapMap;
map<int, vector<HoldReplayResult>> PlayerAI::m_ReplayHoldMap;

void PlayerAI::InitFromDisk()
{
Expand Down Expand Up @@ -123,9 +125,14 @@ TapNoteScore PlayerAI::GetTapNoteScore( const PlayerState* pPlayerState )

TapNoteScore PlayerAI::GetTapNoteScoreForReplay(const PlayerState* pPlayerState, float fNoteOffset)
{
// This code is basically a copy paste from somewhere in Player for grabbing scores.

//LOG->Trace("Given number %f ", fNoteOffset);
if (fNoteOffset <= -1.0f)
return TNS_Miss;
const float fSecondsFromExact = fabsf(fNoteOffset);
//LOG->Trace("TapNoteScore For Replay Seconds From Exact: %f", fSecondsFromExact);

if (fSecondsFromExact <= Player::GetWindowSeconds(TW_W1))
return TNS_W1;
else if (fSecondsFromExact <= Player::GetWindowSeconds(TW_W2))
Expand All @@ -142,7 +149,69 @@ TapNoteScore PlayerAI::GetTapNoteScoreForReplay(const PlayerState* pPlayerState,
void PlayerAI::SetScoreData(HighScore* pHighScore)
{
pHighScore->LoadReplayData();
PlayerAI::pScoreData = pHighScore;
pScoreData = pHighScore;
auto replayNoteRowVector = pHighScore->GetCopyOfNoteRowVector();
auto replayOffsetVector = pHighScore->GetCopyOfOffsetVector();
auto replayTapNoteTypeVector = pHighScore->GetCopyOfTapNoteTypeVector();
auto replayTrackVector = pHighScore->GetCopyOfTrackVector();
auto replayHoldVector = pHighScore->GetCopyOfHoldReplayDataVector();

// Generate TapReplayResults to put into a vector referenced by the song row in a map
for (int i = 0; i < replayTrackVector.size(); i++)
{
TapReplayResult trr;
trr.row = replayNoteRowVector[i];
trr.track = replayTrackVector[i];
trr.offset = replayOffsetVector[i];
trr.type = replayTapNoteTypeVector[i];

// Create or append to the vector
if (m_ReplayTapMap.count(replayNoteRowVector[i]) != 0)
{
m_ReplayTapMap[replayNoteRowVector[i]].push_back(trr);
}
else
{
vector<TapReplayResult> trrVector = { trr };
m_ReplayTapMap[replayNoteRowVector[i]] = trrVector;
}
}

// Generate vectors made of pregenerated HoldReplayResults referenced by the song row in a map
for (int i = 0; i < replayHoldVector.size(); i++)
{
// Create or append to the vector
if (m_ReplayHoldMap.count(replayHoldVector[i].row) != 0)
{
m_ReplayHoldMap[replayHoldVector[i].row].push_back(replayHoldVector[i]);
}
else
{
vector<HoldReplayResult> hrrVector = { replayHoldVector[i] };
m_ReplayHoldMap[replayHoldVector[i].row] = hrrVector;
}
}
}

bool PlayerAI::DetermineIfHoldDropped(int noteRow, int col)
{
//LOG->Trace("Checking for hold.");
// Is the given row/column in our dropped hold map?
if (m_ReplayHoldMap.count(noteRow) != 0)
{
//LOG->Trace("Hold row exists in the data");
// It is, so let's go over each column, assuming we may have dropped more than one hold at once.
for (auto hrr : m_ReplayHoldMap[noteRow])
{
// We found the column we are looking for
if (hrr.track == col)
{
//LOG->Trace("KILL IT NOW");
return true;
}
}
}
return false;
}

float PlayerAI::GetTapNoteOffsetForReplay(TapNote* pTN, int noteRow, int col)
Expand All @@ -151,57 +220,25 @@ float PlayerAI::GetTapNoteOffsetForReplay(TapNote* pTN, int noteRow, int col)
If it is not found, it is a miss. (1.f)
*/
if (pScoreData == nullptr) // possible cheat prevention
return 1.f;
return -1.f;

// Replay Data format: [noterow] [offset] [track] [optional: tap note type]
// Current v0.60 Replay Data format: [noterow] [offset] [track] [optional: tap note type]
// Current v0.60 Replay Data format (H section): H [noterow] [track] [optional: tap note subtype]

vector<int> noteRowVector = pScoreData->GetCopyOfNoteRowVector();
vector<float> offsetVector = pScoreData->GetCopyOfOffsetVector();
vector<TapNoteType> tntVector = pScoreData->GetCopyOfTapNoteTypeVector();
vector<int> trackVector = pScoreData->GetCopyOfTrackVector();
/*std::string s = std::to_string(noteRow);
char const* nr1 = s.c_str();
std::string lmao = std::to_string(noteRowVector.size());
char const* nrsize = lmao.c_str();
LOG->Trace("vector size %s", nrsize);
LOG->Trace("Comparing %s", nr1);*/

for (int i = 0; i < noteRowVector.size(); i++)
if (m_ReplayTapMap.count(noteRow) != 0) // is the current row recorded?
{
/*std::string g = std::to_string(i);
char const* yeet1 = g.c_str();
LOG->Trace(yeet1);*/
if (noteRowVector[i] == noteRow)
for (auto trr : m_ReplayTapMap[noteRow]) // go over all elements in the row
{
//std::string outp = std::to_string(offsetVector[i]);
float outputF = offsetVector[i];
//char const* output = outp.c_str();

if (tntVector.size() > i && tntVector[i] == TapNoteType_Mine)
if (trr.track == col) // if the column expected is the actual note, use it
{
outputF = 2.f;
if (trr.type == TapNoteType_Mine) // hack for mines
return -2.f;
return -trr.offset;
}
else
{
pTN->result.fTapNoteOffset = outputF;
}

noteRowVector.erase(noteRowVector.begin() + i);
offsetVector.erase(offsetVector.begin() + i);
if (tntVector.size() > 0) {
trackVector.erase(trackVector.begin() + i);
tntVector.erase(tntVector.begin() + i);
pScoreData->SetTrackVector(trackVector);
pScoreData->SetTapNoteTypeVector(tntVector);
}
pScoreData->SetNoteRowVector(noteRowVector);
pScoreData->SetOffsetVector(offsetVector);
//LOG->Trace("returned number %s", output);
return -outputF;
}
}
return 0.f;

return -1.f; // data missing or invalid, give them a miss
}

/*
Expand Down
12 changes: 12 additions & 0 deletions src/PlayerAI.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,25 @@ const int NUM_SKILL_LEVELS = 6; // 0-5
class PlayerAI
{
public:
// Pointer to real high score data for a replay

static HighScore* pScoreData;

// Pulled from pScoreData on initialization

// A map with indices for each row of the chart, pointing to nothing or a Normal Result
static map<int, vector<TapReplayResult>> m_ReplayTapMap;
// A map with indices for each row of the chart, pointing to nothing or hold drop results.
static map<int, vector<HoldReplayResult>> m_ReplayHoldMap;

static void InitFromDisk();
static TapNoteScore GetTapNoteScore( const PlayerState* pPlayerState );
static void SetScoreData(HighScore* pHighScore);

static float GetTapNoteOffsetForReplay(TapNote* pTN, int noteRow, int col);
static TapNoteScore GetTapNoteScoreForReplay(const PlayerState* pPlayerState, float fNoteOffset);
static bool DetermineIfHoldDropped(int noteRow, int col);


};

Expand Down
5 changes: 1 addition & 4 deletions src/ScreenEvaluation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,10 +674,7 @@ void ScreenEvaluation::Init()
default:
break;
}
if (GamePreferences::m_AutoPlay == PC_REPLAY)
{
STATSMAN->m_vPlayedStageStats.pop_back();
}

}

bool ScreenEvaluation::Input( const InputEventPlus &input )
Expand Down
4 changes: 2 additions & 2 deletions src/ScreenGameplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2044,8 +2044,8 @@ void ScreenGameplay::StageFinished( bool bBackedOut )
FOREACH_HumanPlayer( pn )
STATSMAN->m_CurStageStats.m_player[pn].CalcAwards( pn, STATSMAN->m_CurStageStats.m_bGaveUp, STATSMAN->m_CurStageStats.m_bUsedAutoplay );
STATSMAN->m_CurStageStats.FinalizeScores( false );
GAMESTATE->CommitStageStats();

if ( GamePreferences::m_AutoPlay == PC_HUMAN )
GAMESTATE->CommitStageStats();
// save current stage stats
STATSMAN->m_vPlayedStageStats.push_back( STATSMAN->m_CurStageStats );

Expand Down

0 comments on commit 0af2f74

Please sign in to comment.