Allow queuing attacks for later.
Add GetAllUsedNoteSkins, SetNoteSkinForBeatRange.
This commit is contained in:
+137
-42
@@ -145,23 +145,46 @@ void GameState::Update( float 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;
|
||||
|
||||
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
|
||||
{
|
||||
if( m_ActiveAttacks[p][s].fStartSecond >= 0 )
|
||||
continue; /* hasn't started yet */
|
||||
|
||||
if( m_ActiveAttacks[p][s].fSecsRemaining <= 0 )
|
||||
continue; /* ended already */
|
||||
|
||||
m_ActiveAttacks[p][s].fSecsRemaining -= fDelta;
|
||||
|
||||
if( m_ActiveAttacks[p][s].fSecsRemaining > 0 )
|
||||
{
|
||||
m_ActiveAttacks[p][s].fSecsRemaining -= fDelta;
|
||||
if( m_ActiveAttacks[p][s].fSecsRemaining <= 0 )
|
||||
{
|
||||
m_ActiveAttacks[p][s].fSecsRemaining = 0;
|
||||
m_ActiveAttacks[p][s].sModifier = "";
|
||||
m_bActiveAttackEndedThisUpdate[p] = true;
|
||||
}
|
||||
}
|
||||
continue; /* continuing */
|
||||
|
||||
/* ending */
|
||||
m_ActiveAttacks[p][s].fSecsRemaining = 0;
|
||||
m_ActiveAttacks[p][s].sModifier = "";
|
||||
m_bActiveAttackEndedThisUpdate[p] = true;
|
||||
RebuildPlayerOptions = true;
|
||||
}
|
||||
|
||||
if( m_bActiveAttackEndedThisUpdate[p] )
|
||||
if( RebuildPlayerOptions )
|
||||
RebuildPlayerOptionsFromActiveAttacks( (PlayerNumber)p );
|
||||
}
|
||||
}
|
||||
@@ -560,6 +583,37 @@ void GameState::ResetNoteSkins()
|
||||
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: */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -585,61 +650,87 @@ void GameState::LaunchAttack( PlayerNumber target, Attack a )
|
||||
//
|
||||
PlayerOptions po;
|
||||
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 );
|
||||
}
|
||||
|
||||
if( po.m_sNoteSkin != "" )
|
||||
{
|
||||
map<float,CString> &BeatToNoteSkin = m_BeatToNoteSkin[target];
|
||||
/* Add it in the future, past what's currently on screen, so new arrows will scroll
|
||||
* on screen with this skin. */
|
||||
float StartBeat, EndBeat;
|
||||
GetUndisplayedBeats( target, a.fSecsRemaining, StartBeat, EndBeat );
|
||||
|
||||
/* If there are any note skins after the point we're adding, remove them. We probably
|
||||
* have overlapping note skin attacks. */
|
||||
map<float,CString>::iterator it = BeatToNoteSkin.begin();
|
||||
while( it != BeatToNoteSkin.end() )
|
||||
if( type == QUEUE_FOR_LATER )
|
||||
{
|
||||
map<float,CString>::iterator next = it;
|
||||
++next;
|
||||
if( it->first >= StartBeat )
|
||||
{
|
||||
LOG->Trace( "erase old %f", it->first );
|
||||
BeatToNoteSkin.erase( it );
|
||||
}
|
||||
it = next;
|
||||
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;
|
||||
GetUndisplayedBeats( target, a.fSecsRemaining, StartBeat, EndBeat );
|
||||
SetNoteSkinForBeatRange( target, po.m_sNoteSkin, StartBeat, EndBeat );
|
||||
}
|
||||
|
||||
/* Add the skin to m_BeatToNoteSkin. */
|
||||
BeatToNoteSkin[StartBeat] = po.m_sNoteSkin;
|
||||
|
||||
/* Return to the default note skin after the duration. */
|
||||
BeatToNoteSkin[EndBeat] = GAMESTATE->m_PlayerOptions[target].m_sNoteSkin;
|
||||
|
||||
++m_BeatToNoteSkinRev;
|
||||
po.m_sNoteSkin = "";
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::SetNoteSkinForBeatRange( PlayerNumber pn, CString sNoteSkin, float StartBeat, float EndBeat )
|
||||
{
|
||||
LOG->Trace("... queue at %f..%f", StartBeat, EndBeat );
|
||||
|
||||
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;
|
||||
++next;
|
||||
|
||||
BeatToNoteSkin.erase( it );
|
||||
|
||||
it = next;
|
||||
}
|
||||
|
||||
/* Add the skin to m_BeatToNoteSkin. */
|
||||
BeatToNoteSkin[StartBeat] = sNoteSkin;
|
||||
|
||||
/* Return to the default note skin after the duration. */
|
||||
BeatToNoteSkin[EndBeat] = m_StoredPlayerOptions[pn].m_sNoteSkin;
|
||||
|
||||
++m_BeatToNoteSkinRev;
|
||||
}
|
||||
|
||||
/* 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
|
||||
for( int s=0; s<MAX_SIMULTANEOUS_ATTACKS; s++ )
|
||||
{
|
||||
if( m_ActiveAttacks[target][s].fSecsRemaining <= 0 )
|
||||
{
|
||||
m_ActiveAttacks[target][s] = a;
|
||||
ActivateAttack( target, s, false );
|
||||
GAMESTATE->RebuildPlayerOptionsFromActiveAttacks( target );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG->Warn("Couldn't launch attack '%s' against p%i: no empty attack slots",
|
||||
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++ )
|
||||
{
|
||||
if( al != NUM_ATTACK_LEVELS && al != m_ActiveAttacks[pn][s].level )
|
||||
continue;
|
||||
m_ActiveAttacks[pn][s].fSecsRemaining = 0;
|
||||
m_ActiveAttacks[pn][s].sModifier = "";
|
||||
}
|
||||
@@ -661,7 +752,11 @@ void GameState::RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn )
|
||||
// rebuild player options
|
||||
PlayerOptions po = m_StoredPlayerOptions[pn];
|
||||
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 );
|
||||
}
|
||||
m_PlayerOptions[pn] = po;
|
||||
}
|
||||
|
||||
@@ -670,7 +765,7 @@ int GameState::GetSumOfActiveAttackLevels( PlayerNumber pn )
|
||||
int iSum = 0;
|
||||
|
||||
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;
|
||||
|
||||
return iSum;
|
||||
|
||||
@@ -152,6 +152,7 @@ public:
|
||||
map<float,CString> m_BeatToNoteSkin[NUM_PLAYERS];
|
||||
int m_BeatToNoteSkinRev; /* hack: incremented whenever m_BeatToNoteSkin changes */
|
||||
void ResetNoteSkins();
|
||||
void GetAllUsedNoteSkins( vector<CString> &out ) const;
|
||||
|
||||
static const float MUSIC_SECONDS_INVALID;
|
||||
|
||||
@@ -170,14 +171,18 @@ public:
|
||||
struct Attack
|
||||
{
|
||||
AttackLevel level;
|
||||
float fStartSecond; // -1 = now
|
||||
float fSecsRemaining;
|
||||
CString sModifier;
|
||||
bool IsBlank() { return sModifier.empty(); }
|
||||
void MakeBlank() { sModifier=""; }
|
||||
Attack() { fStartSecond = -1; }
|
||||
};
|
||||
enum { MAX_SIMULTANEOUS_ATTACKS=16 };
|
||||
Attack m_ActiveAttacks[NUM_PLAYERS][MAX_SIMULTANEOUS_ATTACKS];
|
||||
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
|
||||
Attack m_Inventory[NUM_PLAYERS][NUM_INVENTORY_SLOTS];
|
||||
@@ -196,7 +201,7 @@ public:
|
||||
for( int p=0; p<NUM_PLAYERS; p++ )
|
||||
RemoveActiveAttacksForPlayer( (PlayerNumber)p );
|
||||
}
|
||||
void RemoveActiveAttacksForPlayer( PlayerNumber pn );
|
||||
void RemoveActiveAttacksForPlayer( PlayerNumber pn, AttackLevel al=NUM_ATTACK_LEVELS /*all*/ );
|
||||
void RemoveAllInventory();
|
||||
int GetSumOfActiveAttackLevels( PlayerNumber pn );
|
||||
PlayerNumber GetBestPlayer();
|
||||
|
||||
Reference in New Issue
Block a user