working on player AI
This commit is contained in:
@@ -414,3 +414,15 @@ void GameState::RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn )
|
|||||||
}
|
}
|
||||||
GAMESTATE->m_PlayerOptions[pn] = po;
|
GAMESTATE->m_PlayerOptions[pn] = po;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GameState::GetSumOfActiveAttackLevels( PlayerNumber pn )
|
||||||
|
{
|
||||||
|
int iSum = 0;
|
||||||
|
|
||||||
|
for( unsigned s=0; s<NUM_INVENTORY_SLOTS; s++ )
|
||||||
|
if( m_ActiveAttacks[pn][s].fSecsRemaining > 0 )
|
||||||
|
iSum += m_ActiveAttacks[pn][s].level;
|
||||||
|
|
||||||
|
return iSum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public:
|
|||||||
// used in PLAY_MODE_RAVE and PLAY_MODE_BATTLE
|
// used in PLAY_MODE_RAVE and PLAY_MODE_BATTLE
|
||||||
struct ActiveAttack
|
struct ActiveAttack
|
||||||
{
|
{
|
||||||
|
AttackLevel level;
|
||||||
float fSecsRemaining;
|
float fSecsRemaining;
|
||||||
CString sModifier;
|
CString sModifier;
|
||||||
};
|
};
|
||||||
@@ -172,7 +173,7 @@ public:
|
|||||||
void LaunchAttack( PlayerNumber target, ActiveAttack aa );
|
void LaunchAttack( PlayerNumber target, ActiveAttack aa );
|
||||||
void RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn );
|
void RebuildPlayerOptionsFromActiveAttacks( PlayerNumber pn );
|
||||||
void RemoveAllActiveAttacks(); // called on end of song
|
void RemoveAllActiveAttacks(); // called on end of song
|
||||||
|
int GetSumOfActiveAttackLevels( PlayerNumber pn );
|
||||||
|
|
||||||
|
|
||||||
bool HasEarnedExtraStage();
|
bool HasEarnedExtraStage();
|
||||||
|
|||||||
@@ -21,12 +21,14 @@
|
|||||||
#define ITEM_DURATION_SECONDS THEME->GetMetricF("Inventory","ItemDurationSeconds")
|
#define ITEM_DURATION_SECONDS THEME->GetMetricF("Inventory","ItemDurationSeconds")
|
||||||
#define ITEM_COMBO( i ) THEME->GetMetricI("Inventory",ssprintf("Item%dCombo",i+1))
|
#define ITEM_COMBO( i ) THEME->GetMetricI("Inventory",ssprintf("Item%dCombo",i+1))
|
||||||
#define ITEM_EFFECT( i ) THEME->GetMetric ("Inventory",ssprintf("Item%dEffect",i+1))
|
#define ITEM_EFFECT( i ) THEME->GetMetric ("Inventory",ssprintf("Item%dEffect",i+1))
|
||||||
|
#define ITEM_LEVEL( i ) THEME->GetMetricI("Inventory",ssprintf("Item%dLevel",i+1))
|
||||||
|
|
||||||
const PlayerNumber OPPOSITE_PLAYER[NUM_PLAYERS] = { PLAYER_2, PLAYER_1 };
|
const PlayerNumber OPPOSITE_PLAYER[NUM_PLAYERS] = { PLAYER_2, PLAYER_1 };
|
||||||
|
|
||||||
|
|
||||||
struct Item
|
struct Item
|
||||||
{
|
{
|
||||||
|
AttackLevel level;
|
||||||
int iCombo;
|
int iCombo;
|
||||||
CString sModifier;
|
CString sModifier;
|
||||||
};
|
};
|
||||||
@@ -38,6 +40,7 @@ void ReloadItems()
|
|||||||
for( int i=0; i<NUM_ITEM_TYPES; i++ )
|
for( int i=0; i<NUM_ITEM_TYPES; i++ )
|
||||||
{
|
{
|
||||||
Item item;
|
Item item;
|
||||||
|
item.level = (AttackLevel)(ITEM_LEVEL(i)-1);
|
||||||
item.iCombo = ITEM_COMBO(i);
|
item.iCombo = ITEM_COMBO(i);
|
||||||
item.sModifier = ITEM_EFFECT(i);
|
item.sModifier = ITEM_EFFECT(i);
|
||||||
g_Items.push_back( item );
|
g_Items.push_back( item );
|
||||||
@@ -151,10 +154,20 @@ void Inventory::UseItem( int iSlot )
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
PlayerNumber pnToAttack = OPPOSITE_PLAYER[m_PlayerNumber];
|
PlayerNumber pnToAttack = OPPOSITE_PLAYER[m_PlayerNumber];
|
||||||
|
|
||||||
GameState::ActiveAttack aa;
|
GameState::ActiveAttack aa;
|
||||||
aa.fSecsRemaining = ITEM_DURATION_SECONDS;
|
|
||||||
aa.sModifier = asInventory[iSlot];
|
aa.sModifier = asInventory[iSlot];
|
||||||
|
aa.fSecsRemaining = ITEM_DURATION_SECONDS;
|
||||||
|
aa.level = ATTACK_LEVEL_1;
|
||||||
|
|
||||||
|
// UGLY: Search through g_Items and find out what the attack level for this item is.
|
||||||
|
for( unsigned i=0; i<g_Items.size(); i++ )
|
||||||
|
{
|
||||||
|
if( aa.sModifier == g_Items[i].sModifier )
|
||||||
|
{
|
||||||
|
aa.level = g_Items[i].level;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GAMESTATE->LaunchAttack( pnToAttack, aa );
|
GAMESTATE->LaunchAttack( pnToAttack, aa );
|
||||||
GAMESTATE->RebuildPlayerOptionsFromActiveAttacks( pnToAttack );
|
GAMESTATE->RebuildPlayerOptionsFromActiveAttacks( pnToAttack );
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "Combo.h"
|
#include "Combo.h"
|
||||||
#include "ScoreDisplay.h"
|
#include "ScoreDisplay.h"
|
||||||
#include "LifeMeter.h"
|
#include "LifeMeter.h"
|
||||||
|
#include "PlayerAI.h"
|
||||||
|
|
||||||
#define GRAY_ARROWS_Y THEME->GetMetricF("Player","GrayArrowsY")
|
#define GRAY_ARROWS_Y THEME->GetMetricF("Player","GrayArrowsY")
|
||||||
#define JUDGMENT_Y THEME->GetMetricF("Player","JudgmentY")
|
#define JUDGMENT_Y THEME->GetMetricF("Player","JudgmentY")
|
||||||
@@ -39,75 +40,10 @@ CachedThemeMetricI BRIGHT_GHOST_COMBO_THRESHOLD("Player","BrightGhostComboTh
|
|||||||
#define STOP_DRAWING_AT_PIXELS THEME->GetMetricI("Player","StopDrawingAtPixels")
|
#define STOP_DRAWING_AT_PIXELS THEME->GetMetricI("Player","StopDrawingAtPixels")
|
||||||
|
|
||||||
/* Distance to search for a note in Step(). */
|
/* Distance to search for a note in Step(). */
|
||||||
|
/* Units? */
|
||||||
static const float StepSearchDistanceBackwards = 1.0f;
|
static const float StepSearchDistanceBackwards = 1.0f;
|
||||||
static const float StepSearchDistanceForwards = 1.0f;
|
static const float StepSearchDistanceForwards = 1.0f;
|
||||||
|
|
||||||
struct TapScoreDistribution
|
|
||||||
{
|
|
||||||
float fCumulativeProbability[NUM_TAP_NOTE_SCORES];
|
|
||||||
TapNoteScore GetTapNoteScore()
|
|
||||||
{
|
|
||||||
float fRand = randomf(0,1);
|
|
||||||
for( int i=0; i<NUM_TAP_NOTE_SCORES; i++ )
|
|
||||||
if( fRand <= fCumulativeProbability[i] )
|
|
||||||
return (TapNoteScore)i;
|
|
||||||
ASSERT(0); // the last probability must be 1.0, so we should never get here!
|
|
||||||
return TNS_MARVELOUS;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TapScoreDistribution TAP_SCORE_DISTRIBUTIONS[NUM_PLAYER_CONTROLLERS] =
|
|
||||||
{
|
|
||||||
// HUMAN
|
|
||||||
{
|
|
||||||
0.00f, // TNS_NONE
|
|
||||||
1.00f, // TNS_MISS // don't ever hit automatically when human
|
|
||||||
0.00f, // TNS_BOO
|
|
||||||
0.00f, // TNS_GOOD
|
|
||||||
0.00f, // TNS_GREAT
|
|
||||||
0.00f, // TNS_PERFECT
|
|
||||||
0.00f, // TNS_MARVELOUS
|
|
||||||
},
|
|
||||||
// CPU_EASY
|
|
||||||
{
|
|
||||||
0.00f, // TNS_NONE
|
|
||||||
0.02f, // TNS_MISS
|
|
||||||
0.06f, // TNS_BOO
|
|
||||||
0.10f, // TNS_GOOD
|
|
||||||
0.55f, // TNS_GREAT
|
|
||||||
0.80f, // TNS_PERFECT
|
|
||||||
1.00f, // TNS_MARVELOUS
|
|
||||||
},
|
|
||||||
// CPU_MEDIUM
|
|
||||||
{
|
|
||||||
0.00f, // TNS_NONE
|
|
||||||
0.01f, // TNS_MISS
|
|
||||||
0.02f, // TNS_BOO
|
|
||||||
0.05f, // TNS_GOOD
|
|
||||||
0.45f, // TNS_GREAT
|
|
||||||
0.70f, // TNS_PERFECT
|
|
||||||
1.00f, // TNS_MARVELOUS
|
|
||||||
},
|
|
||||||
// CPU_HARD
|
|
||||||
{
|
|
||||||
0.00f, // TNS_NONE
|
|
||||||
0.005f, // TNS_MISS
|
|
||||||
0.010f, // TNS_BOO
|
|
||||||
0.02f, // TNS_GOOD
|
|
||||||
0.10f, // TNS_GREAT
|
|
||||||
0.50f, // TNS_PERFECT
|
|
||||||
1.00f, // TNS_MARVELOUS
|
|
||||||
},
|
|
||||||
// CPU_AUTOPLAY
|
|
||||||
{
|
|
||||||
0.00f, // TNS_NONE
|
|
||||||
0.00f, // TNS_MISS
|
|
||||||
0.00f, // TNS_BOO
|
|
||||||
0.00f, // TNS_GOOD
|
|
||||||
0.00f, // TNS_GREAT
|
|
||||||
0.00f, // TNS_PERFECT
|
|
||||||
1.00f, // TNS_MARVELOUS
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Player::Player()
|
Player::Player()
|
||||||
@@ -439,7 +375,7 @@ void Player::Step( int col )
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
score = TAP_SCORE_DISTRIBUTIONS[GAMESTATE->m_PlayerController[m_PlayerNumber]].GetTapNoteScore();
|
score = PlayerAI::GetTapNoteScore( GAMESTATE->m_PlayerController[m_PlayerNumber], GAMESTATE->GetSumOfActiveAttackLevels(m_PlayerNumber) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( score==TNS_MARVELOUS && !PREFSMAN->m_bMarvelousTiming )
|
if( score==TNS_MARVELOUS && !PREFSMAN->m_bMarvelousTiming )
|
||||||
@@ -582,7 +518,7 @@ void Player::CrossedRow( int iNoteRow )
|
|||||||
{
|
{
|
||||||
if( GetTapNote(t, iNoteRow) != TAP_EMPTY )
|
if( GetTapNote(t, iNoteRow) != TAP_EMPTY )
|
||||||
{
|
{
|
||||||
TapNoteScore tns = TAP_SCORE_DISTRIBUTIONS[ GAMESTATE->m_PlayerController[m_PlayerNumber] ].GetTapNoteScore();
|
TapNoteScore tns = PlayerAI::GetTapNoteScore( GAMESTATE->m_PlayerController[m_PlayerNumber], GAMESTATE->GetSumOfActiveAttackLevels(m_PlayerNumber) );
|
||||||
if( tns!=TNS_MISS )
|
if( tns!=TNS_MISS )
|
||||||
this->Step( t );
|
this->Step( t );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#include "global.h"
|
||||||
|
/*
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
Class: PlayerAI
|
||||||
|
|
||||||
|
Desc: See header.
|
||||||
|
|
||||||
|
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
|
||||||
|
Chris Danford
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PlayerAI.h"
|
||||||
|
#include "RageUtil.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct TapScoreDistribution
|
||||||
|
{
|
||||||
|
float fCumulativeProbability[NUM_TAP_NOTE_SCORES];
|
||||||
|
TapNoteScore GetTapNoteScore( int iDifficultyExponent )
|
||||||
|
{
|
||||||
|
float fRand = randomf(0,1);
|
||||||
|
ASSERT( iDifficultyExponent >= 1 );
|
||||||
|
fRand = powf(fRand, iDifficultyExponent);
|
||||||
|
for( int i=0; i<NUM_TAP_NOTE_SCORES; i++ )
|
||||||
|
if( fRand <= fCumulativeProbability[i] )
|
||||||
|
return (TapNoteScore)i;
|
||||||
|
ASSERT(0); // the last probability must be 1.0, so we should never get here!
|
||||||
|
return TNS_MARVELOUS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TapScoreDistribution TAP_SCORE_DISTRIBUTIONS[NUM_PLAYER_CONTROLLERS] =
|
||||||
|
{
|
||||||
|
// HUMAN
|
||||||
|
{
|
||||||
|
0.00f, // TNS_NONE
|
||||||
|
1.00f, // TNS_MISS // don't ever hit automatically when human
|
||||||
|
0.00f, // TNS_BOO
|
||||||
|
0.00f, // TNS_GOOD
|
||||||
|
0.00f, // TNS_GREAT
|
||||||
|
0.00f, // TNS_PERFECT
|
||||||
|
0.00f, // TNS_MARVELOUS
|
||||||
|
},
|
||||||
|
// CPU_EASY
|
||||||
|
{
|
||||||
|
0.00f, // TNS_NONE
|
||||||
|
0.02f, // TNS_MISS
|
||||||
|
0.06f, // TNS_BOO
|
||||||
|
0.10f, // TNS_GOOD
|
||||||
|
0.55f, // TNS_GREAT
|
||||||
|
0.80f, // TNS_PERFECT
|
||||||
|
1.00f, // TNS_MARVELOUS
|
||||||
|
},
|
||||||
|
// CPU_MEDIUM
|
||||||
|
{
|
||||||
|
0.00f, // TNS_NONE
|
||||||
|
0.01f, // TNS_MISS
|
||||||
|
0.02f, // TNS_BOO
|
||||||
|
0.05f, // TNS_GOOD
|
||||||
|
0.45f, // TNS_GREAT
|
||||||
|
0.70f, // TNS_PERFECT
|
||||||
|
1.00f, // TNS_MARVELOUS
|
||||||
|
},
|
||||||
|
// CPU_HARD
|
||||||
|
{
|
||||||
|
0.00f, // TNS_NONE
|
||||||
|
0.005f, // TNS_MISS
|
||||||
|
0.010f, // TNS_BOO
|
||||||
|
0.03f, // TNS_GOOD
|
||||||
|
0.10f, // TNS_GREAT
|
||||||
|
0.50f, // TNS_PERFECT
|
||||||
|
1.00f, // TNS_MARVELOUS
|
||||||
|
},
|
||||||
|
// CPU_AUTOPLAY
|
||||||
|
{
|
||||||
|
0.00f, // TNS_NONE
|
||||||
|
0.00f, // TNS_MISS
|
||||||
|
0.00f, // TNS_BOO
|
||||||
|
0.00f, // TNS_GOOD
|
||||||
|
0.00f, // TNS_GREAT
|
||||||
|
0.00f, // TNS_PERFECT
|
||||||
|
1.00f, // TNS_MARVELOUS
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TapNoteScore PlayerAI::GetTapNoteScore( PlayerController pc, int iSumOfAttackLevels )
|
||||||
|
{
|
||||||
|
return TAP_SCORE_DISTRIBUTIONS[pc].GetTapNoteScore( iSumOfAttackLevels+1 );
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef PlayerAI_H
|
||||||
|
#define PlayerAI_H
|
||||||
|
/*
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
Class: PlayerAI
|
||||||
|
|
||||||
|
Desc: .
|
||||||
|
|
||||||
|
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
|
||||||
|
Chris Danford
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GameConstantsAndTypes.h"
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerAI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static TapNoteScore PlayerAI::GetTapNoteScore( PlayerController pc, int iSumOfAttackLevels );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -81,6 +81,7 @@ void RaveHelper::LaunchAttack( AttackLevel al )
|
|||||||
PlayerNumber pnToAttack = OPPOSITE_PLAYER[m_PlayerNumber];
|
PlayerNumber pnToAttack = OPPOSITE_PLAYER[m_PlayerNumber];
|
||||||
|
|
||||||
GameState::ActiveAttack aa;
|
GameState::ActiveAttack aa;
|
||||||
|
aa.level = al;
|
||||||
aa.fSecsRemaining = ATTACK_DURATION_SECONDS;
|
aa.fSecsRemaining = ATTACK_DURATION_SECONDS;
|
||||||
aa.sModifier = sAttackToGive;
|
aa.sModifier = sAttackToGive;
|
||||||
|
|
||||||
|
|||||||
@@ -495,6 +495,14 @@ SOURCE=.\NoteTypes.h
|
|||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\PlayerAI.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\PlayerAI.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
SOURCE=.\PlayerNumber.cpp
|
SOURCE=.\PlayerNumber.cpp
|
||||||
# End Source File
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|||||||
@@ -671,6 +671,12 @@ cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\
|
|||||||
<File
|
<File
|
||||||
RelativePath="NotesWriterSM.h">
|
RelativePath="NotesWriterSM.h">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="PlayerAI.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="PlayerAI.h">
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="PlayerNumber.cpp">
|
RelativePath="PlayerNumber.cpp">
|
||||||
</File>
|
</File>
|
||||||
|
|||||||
Reference in New Issue
Block a user