Allow queuing attacks for later.
Add GetAllUsedNoteSkins, SetNoteSkinForBeatRange.
This commit is contained in:
+121
-26
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user