Allow queuing attacks for later.

Add GetAllUsedNoteSkins, SetNoteSkinForBeatRange.
This commit is contained in:
Glenn Maynard
2003-10-24 09:35:56 +00:00
parent bd51930076
commit 3af0a20a4c
2 changed files with 143 additions and 43 deletions
+121 -26
View File
@@ -145,23 +145,46 @@ void GameState::Update( float fDelta )
{ {
m_CurrentPlayerOptions[p].Approach( m_PlayerOptions[p], fDelta ); m_CurrentPlayerOptions[p].Approach( m_PlayerOptions[p], fDelta );
bool RebuildPlayerOptions = false;
/* See if any delayed attacks are starting. */
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
{
if( m_ActiveAttacks[p][s].fStartSecond < 0 )
continue; /* already started */
if( m_ActiveAttacks[p][s].fStartSecond > this->m_fMusicSeconds )
continue; /* not yet */
m_ActiveAttacks[p][s].fStartSecond = -1;
ActivateAttack( (PlayerNumber)p, s, true );
RebuildPlayerOptions = true;
}
/* See if any attacks are ending. */
m_bActiveAttackEndedThisUpdate[p] = false; m_bActiveAttackEndedThisUpdate[p] = false;
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ ) for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
{ {
if( m_ActiveAttacks[p][s].fSecsRemaining > 0 ) if( m_ActiveAttacks[p][s].fStartSecond >= 0 )
{ continue; /* hasn't started yet */
m_ActiveAttacks[p][s].fSecsRemaining -= fDelta;
if( m_ActiveAttacks[p][s].fSecsRemaining <= 0 ) if( m_ActiveAttacks[p][s].fSecsRemaining <= 0 )
{ continue; /* ended already */
m_ActiveAttacks[p][s].fSecsRemaining -= fDelta;
if( m_ActiveAttacks[p][s].fSecsRemaining > 0 )
continue; /* continuing */
/* ending */
m_ActiveAttacks[p][s].fSecsRemaining = 0; m_ActiveAttacks[p][s].fSecsRemaining = 0;
m_ActiveAttacks[p][s].sModifier = ""; m_ActiveAttacks[p][s].sModifier = "";
m_bActiveAttackEndedThisUpdate[p] = true; m_bActiveAttackEndedThisUpdate[p] = true;
} RebuildPlayerOptions = true;
}
} }
if( m_bActiveAttackEndedThisUpdate[p] ) if( RebuildPlayerOptions )
RebuildPlayerOptionsFromActiveAttacks( (PlayerNumber)p ); RebuildPlayerOptionsFromActiveAttacks( (PlayerNumber)p );
} }
} }
@@ -560,6 +583,37 @@ void GameState::ResetNoteSkins()
m_BeatToNoteSkinRev = 0; m_BeatToNoteSkinRev = 0;
} }
void GameState::GetAllUsedNoteSkins( vector<CString> &out ) const
{
for( int pn=0; pn<NUM_PLAYERS; ++pn )
{
out.push_back( GAMESTATE->m_PlayerOptions[pn].m_sNoteSkin );
switch( GAMESTATE->m_PlayMode )
{
case PLAY_MODE_BATTLE:
case PLAY_MODE_RAVE:
for( int al=0; al<NUM_ATTACK_LEVELS; al++ )
{
const Character *ch = GAMESTATE->m_pCurCharacters[pn];
ASSERT( ch );
const CString* asAttacks = ch->m_sAttacks[al];
for( int att = 0; att < NUM_ATTACKS_PER_LEVEL; ++att )
{
PlayerOptions po;
po.FromString( asAttacks[att] );
if( po.m_sNoteSkin != "" )
out.push_back( po.m_sNoteSkin );
}
}
}
for( map<float,CString>::const_iterator it = m_BeatToNoteSkin[pn].begin();
it != m_BeatToNoteSkin[pn].end(); ++it )
out.push_back( it->second );
}
}
/* From NoteField: */ /* From NoteField: */
void GameState::GetUndisplayedBeats( PlayerNumber pn, float TotalSeconds, float &StartBeat, float &EndBeat ) void GameState::GetUndisplayedBeats( PlayerNumber pn, float TotalSeconds, float &StartBeat, float &EndBeat )
@@ -574,9 +628,20 @@ void GameState::GetUndisplayedBeats( PlayerNumber pn, float TotalSeconds, float
EndBeat = truncf(EndBeat)+1; EndBeat = truncf(EndBeat)+1;
} }
void GameState::LaunchAttack( PlayerNumber target, Attack a ) void GameState::ActivateAttack( PlayerNumber target, int slot, bool ActivingDelayedAttack )
{ {
LOG->Trace( "Launch attack '%s' against P%d", a.sModifier.c_str(), target+1 ); const Attack &a = m_ActiveAttacks[target][slot];
enum { QUEUE_FOR_LATER, ACTIVATE_QUEUED_ATTACK, START_IMMEDIATELY } type;
if( ActivingDelayedAttack )
{
ASSERT( a.fStartSecond < 0 );
type = ACTIVATE_QUEUED_ATTACK;
}
else if( a.fStartSecond >= 0 )
type = QUEUE_FOR_LATER;
else
type = START_IMMEDIATELY;
// //
// Peek at the effect being applied. If it's a transform, add it to // Peek at the effect being applied. If it's a transform, add it to
@@ -585,61 +650,87 @@ void GameState::LaunchAttack( PlayerNumber target, Attack a )
// //
PlayerOptions po; PlayerOptions po;
po.FromString( a.sModifier ); po.FromString( a.sModifier );
if( po.m_Transform != PlayerOptions::TRANSFORM_NONE ) if( type != QUEUE_FOR_LATER && po.m_Transform != PlayerOptions::TRANSFORM_NONE )
{ {
m_TransformsToApply[target].push_back( po.m_Transform ); m_TransformsToApply[target].push_back( po.m_Transform );
} }
if( po.m_sNoteSkin != "" ) if( po.m_sNoteSkin != "" )
{ {
map<float,CString> &BeatToNoteSkin = m_BeatToNoteSkin[target]; if( type == QUEUE_FOR_LATER )
/* Add it in the future, past what's currently on screen, so new arrows will scroll {
* on screen with this skin. */ float StartBeat = this->m_pCurSong->GetBeatFromElapsedTime( a.fStartSecond );
float EndBeat = this->m_pCurSong->GetBeatFromElapsedTime( a.fStartSecond+a.fSecsRemaining );
LOG->Trace("... x queue at %f..%f", StartBeat, EndBeat );
SetNoteSkinForBeatRange( target, po.m_sNoteSkin, StartBeat, EndBeat );
}
else if( type == START_IMMEDIATELY )
{
/* We're changing note skins on the fly. Add it in the future, past what's
* currently on screen, so new arrows will scroll on screen with this skin. */
float StartBeat, EndBeat; float StartBeat, EndBeat;
GetUndisplayedBeats( target, a.fSecsRemaining, StartBeat, EndBeat ); GetUndisplayedBeats( target, a.fSecsRemaining, StartBeat, EndBeat );
SetNoteSkinForBeatRange( target, po.m_sNoteSkin, StartBeat, EndBeat );
}
}
}
/* If there are any note skins after the point we're adding, remove them. We probably void GameState::SetNoteSkinForBeatRange( PlayerNumber pn, CString sNoteSkin, float StartBeat, float EndBeat )
* have overlapping note skin attacks. */ {
map<float,CString>::iterator it = BeatToNoteSkin.begin(); LOG->Trace("... queue at %f..%f", StartBeat, EndBeat );
while( it != BeatToNoteSkin.end() )
map<float,CString> &BeatToNoteSkin = m_BeatToNoteSkin[pn];
/* Erase any other note skin settings in this range. */
map<float,CString>::iterator it = BeatToNoteSkin.lower_bound( StartBeat );
map<float,CString>::iterator end = BeatToNoteSkin.upper_bound( EndBeat );
while( it != end )
{ {
map<float,CString>::iterator next = it; map<float,CString>::iterator next = it;
++next; ++next;
if( it->first >= StartBeat )
{
LOG->Trace( "erase old %f", it->first );
BeatToNoteSkin.erase( it ); BeatToNoteSkin.erase( it );
}
it = next; it = next;
} }
/* Add the skin to m_BeatToNoteSkin. */ /* Add the skin to m_BeatToNoteSkin. */
BeatToNoteSkin[StartBeat] = po.m_sNoteSkin; BeatToNoteSkin[StartBeat] = sNoteSkin;
/* Return to the default note skin after the duration. */ /* Return to the default note skin after the duration. */
BeatToNoteSkin[EndBeat] = GAMESTATE->m_PlayerOptions[target].m_sNoteSkin; BeatToNoteSkin[EndBeat] = m_StoredPlayerOptions[pn].m_sNoteSkin;
++m_BeatToNoteSkinRev; ++m_BeatToNoteSkinRev;
po.m_sNoteSkin = "";
} }
/* This is called to launch an attack, or to queue an attack if a.fStartSecond
* is set. This is also called by GameState::Update when activating a queued attack. */
void GameState::LaunchAttack( PlayerNumber target, Attack a )
{
LOG->Trace( "Launch attack '%s' against P%d at %f", a.sModifier.c_str(), target+1, a.fStartSecond );
// search for an open slot // search for an open slot
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ ) for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
{
if( m_ActiveAttacks[target][s].fSecsRemaining <= 0 ) if( m_ActiveAttacks[target][s].fSecsRemaining <= 0 )
{ {
m_ActiveAttacks[target][s] = a; m_ActiveAttacks[target][s] = a;
ActivateAttack( target, s, false );
GAMESTATE->RebuildPlayerOptionsFromActiveAttacks( target ); GAMESTATE->RebuildPlayerOptionsFromActiveAttacks( target );
return; return;
} }
}
LOG->Warn("Couldn't launch attack '%s' against p%i: no empty attack slots", LOG->Warn("Couldn't launch attack '%s' against p%i: no empty attack slots",
a.sModifier.c_str(), target ); a.sModifier.c_str(), target );
} }
void GameState::RemoveActiveAttacksForPlayer( PlayerNumber pn ) void GameState::RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al )
{ {
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ ) for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
{ {
if( al != NUM_ATTACK_LEVELS && al != m_ActiveAttacks[pn][s].level )
continue;
m_ActiveAttacks[pn][s].fSecsRemaining = 0; m_ActiveAttacks[pn][s].fSecsRemaining = 0;
m_ActiveAttacks[pn][s].sModifier = ""; m_ActiveAttacks[pn][s].sModifier = "";
} }
@@ -661,7 +752,11 @@ void GameState::RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn )
// rebuild player options // rebuild player options
PlayerOptions po = m_StoredPlayerOptions[pn]; PlayerOptions po = m_StoredPlayerOptions[pn];
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ ) for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
{
if( m_ActiveAttacks[pn][s].fStartSecond >= 0 )
continue; /* hasn't started yet */
po.FromString( m_ActiveAttacks[pn][s].sModifier ); po.FromString( m_ActiveAttacks[pn][s].sModifier );
}
m_PlayerOptions[pn] = po; m_PlayerOptions[pn] = po;
} }
@@ -670,7 +765,7 @@ int GameState::GetSumOfActiveAttackLevels( PlayerNumber pn )
int iSum = 0; int iSum = 0;
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ ) for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
if( m_ActiveAttacks[pn][s].fSecsRemaining > 0 ) if( m_ActiveAttacks[pn][s].fSecsRemaining > 0 && m_ActiveAttacks[pn][s].level != NUM_ATTACK_LEVELS )
iSum += m_ActiveAttacks[pn][s].level; iSum += m_ActiveAttacks[pn][s].level;
return iSum; return iSum;
+6 -1
View File
@@ -152,6 +152,7 @@ public:
map<float,CString> m_BeatToNoteSkin[NUM_PLAYERS]; map<float,CString> m_BeatToNoteSkin[NUM_PLAYERS];
int m_BeatToNoteSkinRev; /* hack: incremented whenever m_BeatToNoteSkin changes */ int m_BeatToNoteSkinRev; /* hack: incremented whenever m_BeatToNoteSkin changes */
void ResetNoteSkins(); void ResetNoteSkins();
void GetAllUsedNoteSkins( vector<CString> &out ) const;
static const float MUSIC_SECONDS_INVALID; static const float MUSIC_SECONDS_INVALID;
@@ -170,14 +171,18 @@ public:
struct Attack struct Attack
{ {
AttackLevel level; AttackLevel level;
float fStartSecond; // -1 = now
float fSecsRemaining; float fSecsRemaining;
CString sModifier; CString sModifier;
bool IsBlank() { return sModifier.empty(); } bool IsBlank() { return sModifier.empty(); }
void MakeBlank() { sModifier=""; } void MakeBlank() { sModifier=""; }
Attack() { fStartSecond = -1; }
}; };
enum { MAX_SIMULTANEOUS_ATTACKS=16 }; enum { MAX_SIMULTANEOUS_ATTACKS=16 };
Attack m_ActiveAttacks[NUM_PLAYERS][MAX_SIMULTANEOUS_ATTACKS]; Attack m_ActiveAttacks[NUM_PLAYERS][MAX_SIMULTANEOUS_ATTACKS];
vector<PlayerOptions::Transform> m_TransformsToApply[NUM_PLAYERS]; vector<PlayerOptions::Transform> m_TransformsToApply[NUM_PLAYERS];
void ActivateAttack( PlayerNumber target, int slot, bool ActivingDelayedAttack );
void SetNoteSkinForBeatRange( PlayerNumber pn, CString sNoteSkin, float StartBeat, float EndBeat );
// used in PLAY_MODE_BATTLE // used in PLAY_MODE_BATTLE
Attack m_Inventory[NUM_PLAYERS][NUM_INVENTORY_SLOTS]; Attack m_Inventory[NUM_PLAYERS][NUM_INVENTORY_SLOTS];
@@ -196,7 +201,7 @@ public:
for( int p=0; p<NUM_PLAYERS; p++ ) for( int p=0; p<NUM_PLAYERS; p++ )
RemoveActiveAttacksForPlayer( (PlayerNumber)p ); RemoveActiveAttacksForPlayer( (PlayerNumber)p );
} }
void RemoveActiveAttacksForPlayer( PlayerNumber pn ); void RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al=NUM_ATTACK_LEVELS /*all*/ );
void RemoveAllInventory(); void RemoveAllInventory();
int GetSumOfActiveAttackLevels( PlayerNumber pn ); int GetSumOfActiveAttackLevels( PlayerNumber pn );
PlayerNumber GetBestPlayer(); PlayerNumber GetBestPlayer();