rework tap attack representation
This commit is contained in:
+52
-28
@@ -174,40 +174,66 @@ void NoteData::RemoveHoldNote( int iHoldIndex )
|
||||
m_HoldNotes.erase(m_HoldNotes.begin()+iHoldIndex, m_HoldNotes.begin()+iHoldIndex+1);
|
||||
}
|
||||
|
||||
void NoteData::AddAttackNote( AttackNote an )
|
||||
void NoteData::AddAttackNote( int track, int row, Attack attack )
|
||||
{
|
||||
m_AttackNotes.push_back(an);
|
||||
}
|
||||
PruneUnusedAttacksFromMap();
|
||||
|
||||
void NoteData::RemoveAttackNote( int index )
|
||||
{
|
||||
m_AttackNotes.erase( m_AttackNotes.begin()+index );
|
||||
}
|
||||
|
||||
bool CompareAttackNotes( const AttackNote& an1, const AttackNote& an2 )
|
||||
{
|
||||
return an1.fBeat < an2.fBeat;
|
||||
}
|
||||
|
||||
void NoteData::ShuffleAttackNotesOnSameRow()
|
||||
{
|
||||
// sort, then shuffle
|
||||
sort( m_AttackNotes.begin(), m_AttackNotes.end(), CompareAttackNotes );
|
||||
|
||||
for( int i=0; i<m_AttackNotes.size(); )
|
||||
// find first unused attack index
|
||||
TapNote tn;
|
||||
for( tn = TAP_ATTACK_BEGIN; tn<=TAP_ATTACK_END; tn++ )
|
||||
{
|
||||
// Shuffle with other AttackNotes that are on the same row.
|
||||
// Lame shuffle...
|
||||
for( int j=i+1; j<m_AttackNotes.size(); j++ )
|
||||
if( m_AttackMap.find(tn) == m_AttackMap.end() ) // this TapNote value is free to use
|
||||
goto done_searching;
|
||||
}
|
||||
ASSERT(0);
|
||||
|
||||
done_searching:
|
||||
|
||||
m_AttackMap[tn] = attack;
|
||||
SetTapNote( track, row, tn );
|
||||
}
|
||||
|
||||
void NoteData::PruneUnusedAttacksFromMap()
|
||||
{
|
||||
// Add all used AttackNote values to a map.
|
||||
map<TapNote,int> mapAttackToNothing;
|
||||
|
||||
int max_row = GetMaxRow();
|
||||
for( int t=0; t<m_iNumTracks; t++ )
|
||||
{
|
||||
for( int r=0; r<=max_row; r++ )
|
||||
{
|
||||
if( m_AttackNotes[i].fBeat != m_AttackNotes[j].fBeat )
|
||||
break; // stop searching
|
||||
if( rand()%2 )
|
||||
swap( m_AttackNotes[i].fBeat, m_AttackNotes[j].fBeat );
|
||||
TapNote tn = GetTapNote(t, r);
|
||||
if( IsTapAttack( tn ) )
|
||||
mapAttackToNothing[tn] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all items from m_AttackMap that don't have corresponding
|
||||
// TapNotes in use.
|
||||
for( TapNote tn = TAP_ATTACK_BEGIN; tn<=TAP_ATTACK_END; tn++ )
|
||||
{
|
||||
bool bInAttackMap = m_AttackMap.find(tn) != m_AttackMap.end();
|
||||
bool bActuallyUsed = mapAttackToNothing.find(tn) != mapAttackToNothing.end();
|
||||
|
||||
if( bActuallyUsed && !bInAttackMap )
|
||||
ASSERT( 0 );
|
||||
|
||||
if( bInAttackMap && !bActuallyUsed )
|
||||
{
|
||||
m_AttackMap.erase( tn );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Attack& NoteData::GetAttackAt( int track, int row )
|
||||
{
|
||||
TapNote tn = GetTapNote(track, row);
|
||||
ASSERT( IsTapAttack(tn) );
|
||||
return m_AttackMap[tn];
|
||||
}
|
||||
|
||||
|
||||
int NoteData::GetFirstRow() const
|
||||
{
|
||||
return BeatToNoteRow( GetFirstBeat() );
|
||||
@@ -677,5 +703,3 @@ int NoteData::GetNumTracksHeldAtRow( int row )
|
||||
GetTracksHeldAtRow( row, viTracks );
|
||||
return viTracks.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
#include "NoteTypes.h"
|
||||
#include <map>
|
||||
#include "Attack.h"
|
||||
|
||||
|
||||
class NoteData
|
||||
@@ -25,7 +27,7 @@ class NoteData
|
||||
|
||||
vector<HoldNote> m_HoldNotes;
|
||||
|
||||
vector<AttackNote> m_AttackNotes;
|
||||
map<TapNote,Attack> m_AttackMap;
|
||||
|
||||
/* Pad m_TapNotes so it includes the row "rows". */
|
||||
void PadTapNotes(int rows);
|
||||
@@ -134,20 +136,22 @@ public:
|
||||
void GetTracksHeldAtRow( int row, vector<int>& viTracksOut );
|
||||
int GetNumTracksHeldAtRow( int row );
|
||||
|
||||
//
|
||||
// used in edit/record
|
||||
//
|
||||
void AddHoldNote( HoldNote newNote ); // add note hold note merging overlapping HoldNotes and destroying TapNotes underneath
|
||||
void RemoveHoldNote( int index );
|
||||
HoldNote &GetHoldNote( int index ) { return m_HoldNotes[index]; }
|
||||
const HoldNote &GetHoldNote( int index ) const { return m_HoldNotes[index]; }
|
||||
|
||||
// used in edit/record
|
||||
void AddAttackNote( AttackNote an ); // add note hold note merging overlapping HoldNotes and destroying TapNotes underneath
|
||||
void RemoveAttackNote( int index );
|
||||
const AttackNote& GetAttackNote( int index ) { return m_AttackNotes[index]; }
|
||||
void ShuffleAttackNotesOnSameRow();
|
||||
void AddAttackNote( int track, int row, Attack attack );
|
||||
void PruneUnusedAttacksFromMap(); // slow
|
||||
const Attack& GetAttackAt( int track, int row );
|
||||
// remove Attacks with SetTapNote(TAP_EMPTY)
|
||||
|
||||
//
|
||||
// statistics
|
||||
|
||||
//
|
||||
/* Return the highest beat/row that might contain notes. (Use GetLastBeat if you need
|
||||
* accuracy.) */
|
||||
float GetMaxBeat() const { return NoteRowToBeat(GetMaxRow()); }
|
||||
@@ -164,7 +168,6 @@ public:
|
||||
/* optimization: for the default of start to end, use the second (faster) */
|
||||
int GetNumHoldNotes( const float fStartBeat, const float fEndBeat = -1 ) const;
|
||||
int GetNumHoldNotes() const { return m_HoldNotes.size(); }
|
||||
int GetNumAttackNotes() const { return m_AttackNotes.size(); }
|
||||
|
||||
// Transformations
|
||||
void LoadTransformed( const NoteData* pOriginal, int iNewNumTracks, const int iOriginalTrackToTakeFrom[] ); // -1 for iOriginalTracksToTakeFrom means no track
|
||||
|
||||
+13
-27
@@ -543,38 +543,24 @@ void NoteField::DrawPrimitives()
|
||||
|
||||
bool bIsAddition = (tn == TAP_ADDITION);
|
||||
bool bIsMine = (tn == TAP_MINE);
|
||||
bool bIsAttack = IsTapAttack(tn);
|
||||
|
||||
SearchForBeat( CurDisplay, NextDisplay, NoteRowToBeat(i) );
|
||||
NoteDisplayCols *nd = CurDisplay->second;
|
||||
nd->display[c].DrawTap( c, NoteRowToBeat(i), bHoldNoteBeginsOnThisBeat, bIsAddition, bIsMine, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, 1, m_fYReverseOffsetPixels );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Draw all AttackNotes in this column
|
||||
//
|
||||
for( i=0; i < GetNumAttackNotes(); i++ )
|
||||
{
|
||||
const AttackNote& an = GetAttackNote( i );
|
||||
|
||||
// If this AttackNote isn't on the screen, skip it
|
||||
if( an.iTrack != c ||
|
||||
an.fBeat < fFirstBeatToDraw ||
|
||||
an.fBeat > fLastBeatToDraw )
|
||||
continue; // skip
|
||||
|
||||
bool bIsInSelectionRange = false;
|
||||
if( m_fBeginMarker!=-1 && m_fEndMarker!=-1 )
|
||||
if( bIsAttack )
|
||||
{
|
||||
bIsInSelectionRange = m_fBeginMarker<=an.fBeat && an.fBeat<=m_fEndMarker;
|
||||
const Attack& attack = GetAttackAt( c, i );
|
||||
Sprite sprite;
|
||||
sprite.Load( THEME->GetPathToG("NoteField attack "+attack.sModifier) );
|
||||
float fBeat = NoteRowToBeat(i);
|
||||
SearchForBeat( CurDisplay, NextDisplay, fBeat );
|
||||
NoteDisplayCols *nd = CurDisplay->second;
|
||||
nd->display[c].DrawActor( &sprite, c, fBeat, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, 1, m_fYReverseOffsetPixels );
|
||||
}
|
||||
else
|
||||
{
|
||||
nd->display[c].DrawTap( c, NoteRowToBeat(i), bHoldNoteBeginsOnThisBeat, bIsAddition, bIsMine, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, 1, m_fYReverseOffsetPixels );
|
||||
}
|
||||
|
||||
|
||||
Sprite sprite;
|
||||
sprite.Load( THEME->GetPathToG("NoteField attack "+an.sModifiers) );
|
||||
SearchForBeat( CurDisplay, NextDisplay, an.fBeat );
|
||||
NoteDisplayCols *nd = CurDisplay->second;
|
||||
nd->display[c].DrawActor( &sprite, an.iTrack, an.fBeat, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail, 1, m_fYReverseOffsetPixels );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -70,6 +70,11 @@ static const TapNote TAP_REMOVED_HOLD_TAIL = '\'';
|
||||
|
||||
// MD 11/12/03 end
|
||||
|
||||
// mine note - don't step!
|
||||
static const TapNote TAP_ATTACK_BEGIN = 'a';
|
||||
static const TapNote TAP_ATTACK_END = 'z';
|
||||
|
||||
inline bool IsTapAttack( TapNote tn ) { return tn>=TAP_ATTACK_BEGIN && tn<=TAP_ATTACK_END; }
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -132,15 +137,6 @@ struct HoldNote
|
||||
float fEndBeat;
|
||||
};
|
||||
|
||||
struct AttackNote
|
||||
{
|
||||
AttackNote( int t, float b, CString m, float d ) { iTrack=t; fBeat=b; sModifiers=m; fDurationSeconds=d; }
|
||||
int iTrack;
|
||||
float fBeat;
|
||||
CString sModifiers;
|
||||
float fDurationSeconds;
|
||||
};
|
||||
|
||||
|
||||
inline int BeatToNoteRow( float fBeatNum ) { return int( fBeatNum * ROWS_PER_BEAT + 0.5f); }; // round
|
||||
inline int BeatToNoteRowNotRounded( float fBeatNum ) { return (int)( fBeatNum * ROWS_PER_BEAT ); };
|
||||
|
||||
@@ -495,6 +495,9 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
|
||||
ASSERT( col >= 0 && col <= GetNumTracks() );
|
||||
|
||||
//
|
||||
// Check for step on a TapNote
|
||||
//
|
||||
int iIndexOverlappingNote = GetClosestNote( col, GAMESTATE->m_fSongBeat,
|
||||
StepSearchDistanceForwards * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.m_fMusicRate,
|
||||
StepSearchDistanceBackwards * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.m_fMusicRate );
|
||||
@@ -557,8 +560,23 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
}
|
||||
|
||||
|
||||
if( tn == TAP_MINE )
|
||||
switch( tn )
|
||||
{
|
||||
// case TAP_ATTACK:
|
||||
// // stepped too close to mine?
|
||||
// if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowAttackSeconds )
|
||||
// {
|
||||
// m_soundAttack.Play();
|
||||
// score = TNS_MISS;
|
||||
// // put attack in effect
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// score = TNS_NONE;
|
||||
// }
|
||||
// break;
|
||||
|
||||
case TAP_MINE:
|
||||
// stepped too close to mine?
|
||||
if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowMineSeconds )
|
||||
{
|
||||
@@ -575,9 +593,9 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
{
|
||||
score = TNS_NONE;
|
||||
}
|
||||
}
|
||||
else // not a mine
|
||||
{
|
||||
break;
|
||||
|
||||
default: // not a mine
|
||||
if(GAMESTATE->m_CurGame == GAME_EZ2)
|
||||
{
|
||||
/* 1 is normal. 2 means scoring is half as hard; .5 means it's twice as hard. */
|
||||
@@ -599,6 +617,7 @@ void PlayerMinus::Step( int col, RageTimer tm )
|
||||
else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowBooSeconds ) score = TNS_BOO;
|
||||
else score = TNS_NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -677,17 +677,6 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
}
|
||||
}
|
||||
|
||||
// check for to see if the user intended to remove an AttackNote
|
||||
for( i=0; i<m_NoteFieldEdit.GetNumAttackNotes(); i++ )
|
||||
{
|
||||
const AttackNote& an = m_NoteFieldEdit.GetAttackNote(i);
|
||||
if( an.fBeat == fSongBeat && an.iTrack == iCol )
|
||||
{
|
||||
m_NoteFieldEdit.RemoveAttackNote( i );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_NoteFieldEdit.GetTapNote(iCol, iSongIndex) != TAP_EMPTY )
|
||||
{
|
||||
m_NoteFieldEdit.SetTapNote( iCol, iSongIndex, TAP_EMPTY );
|
||||
@@ -1286,7 +1275,14 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
|
||||
{
|
||||
PlayerOptions poChosen = GAMESTATE->m_PlayerOptions[PLAYER_1];
|
||||
CString sMods = poChosen.GetString();
|
||||
m_NoteFieldEdit.AddAttackNote( AttackNote(g_iLastInsertAttackTrack,GAMESTATE->m_fSongBeat,sMods,g_fLastInsertAttackDurationSeconds) );
|
||||
const int iSongIndex = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
||||
|
||||
Attack attack;
|
||||
attack.level = ATTACK_LEVEL_1; // does this matter?
|
||||
attack.fSecsRemaining = g_fLastInsertAttackDurationSeconds;
|
||||
attack.sModifier = sMods;
|
||||
|
||||
m_NoteFieldEdit.AddAttackNote( g_iLastInsertAttackTrack, iSongIndex, attack );
|
||||
GAMESTATE->RestoreSelectedOptions(); // restore the edit and playback options
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user