1
0
forked from 0ad/0ad
0ad/source/simulation/Stance.cpp
Matei aa50820d9a #Unit AI: Aggressive, defensive and stand ground stances.
Also split Entity.cpp further, moving the rendering code to
EntityRendering.cpp and the JS interface code to
EntityScriptInterface.cpp. This makes each individual file smaller
(about 1000 lines still!) and lets them be compiled independently.

This was SVN commit r4314.
2006-09-09 00:00:23 +00:00

169 lines
4.4 KiB
C++

#include "precompiled.h"
#include "Stance.h"
#include "EntityManager.h"
#include "Entity.h"
#include "ps/Player.h"
#include "graphics/Terrain.h"
#include <algorithm>
// AggressStance ////////////////////////////////////////////////////
void CAggressStance::onIdle()
{
CEntity* target = CStanceUtils::chooseTarget( m_Entity );
if( target )
CStanceUtils::attack( m_Entity, target );
}
void CAggressStance::onDamaged(CEntity *source)
{
if( source && m_Entity->m_orderQueue.empty() )
CStanceUtils::attack( m_Entity, source );
}
// StandStance //////////////////////////////////////////////////////
void CStandStance::onIdle()
{
CEntity* target = CStanceUtils::chooseTarget( m_Entity );
if( target )
CStanceUtils::attack( m_Entity, target );
}
// DefendStance /////////////////////////////////////////////////////
void CDefendStance::onIdle()
{
idlePos = CVector2D( m_Entity->m_position.X, m_Entity->m_position.Z );
CEntity* target = CStanceUtils::chooseTarget( m_Entity );
if( target )
CStanceUtils::attack( m_Entity, target );
}
void CDefendStance::onDamaged(CEntity *source)
{
if( source && m_Entity->m_orderQueue.empty() )
{
// Retaliate only if we can reach the enemy unit without walking farther than our LOS
// radius away from idlePos.
int action = m_Entity->GetAttackAction( source->me );
if( action )
{
float range = m_Entity->m_actions[action].m_MaxRange;
if( ( range + m_Entity->m_los * CELL_SIZE ) >= m_Entity->distance2D( source ) )
{
CEntityOrder order( CEntityOrder::ORDER_GENERIC, CEntityOrder::SOURCE_UNIT_AI );
order.m_data[0].entity = source->me;
order.m_data[1].data = action;
m_Entity->pushOrder( order );
}
}
}
}
bool CDefendStance::checkMovement( CVector2D proposedPos )
{
float los = m_Entity->m_los*CELL_SIZE;
// Check that we haven't moved too far from the place where we were stationed.
if( (proposedPos - idlePos).length() > los )
{
// TODO: Make sure we don't clear any player orders here; the best way would be to make
// shift-clicked player orders either unqueue any AI orders or convert those AI orders
// to player orders (since the player wants us to finish our attack then do other stuff).
m_Entity->m_orderQueue.clear();
// Try to find some other nearby enemy to attack, provided it's also in range of our idle spot
// TODO: really we should be attack-moving to our spot somehow
std::vector<CEntity*> results;
g_EntityManager.GetInRange( m_Entity->m_position.X, m_Entity->m_position.Z, los, results );
float bestDist = 1e20f;
CEntity* bestTarget = 0;
for( size_t i=0; i<results.size(); i++ )
{
CEntity* ent = results[i];
float range = m_Entity->m_actions[m_Entity->GetAttackAction(ent->me)].m_MaxRange;
if( m_Entity->GetPlayer()->GetDiplomaticStance( ent->GetPlayer() ) == DIPLOMACY_ENEMY )
{
float distToMe = ent->distance2D( m_Entity );
float distToIdlePos = ent->distance2D( idlePos );
if( distToIdlePos <= los+range && distToMe < bestDist )
{
bestDist = distToMe;
bestTarget = ent;
}
}
}
if( bestTarget != 0 )
{
CStanceUtils::attack( m_Entity, bestTarget );
}
else
{
// Let's just walk back to our idle spot
CEntityOrder order( CEntityOrder::ORDER_GOTO, CEntityOrder::SOURCE_UNIT_AI );
order.m_data[0].location = idlePos;
m_Entity->pushOrder( order );
}
return false;
}
else
{
return true;
}
}
// StanceUtils //////////////////////////////////////////////////////
void CStanceUtils::attack(CEntity* entity, CEntity* target)
{
int action = entity->GetAttackAction( target->me );
if( action )
{
CEntityOrder order( CEntityOrder::ORDER_GENERIC, CEntityOrder::SOURCE_UNIT_AI );
order.m_data[0].entity = target->me;
order.m_data[1].data = action;
entity->pushOrder( order );
}
}
CEntity* CStanceUtils::chooseTarget( CEntity* entity )
{
return chooseTarget( entity->m_position.X, entity->m_position.Z, entity->m_los*CELL_SIZE, entity->GetPlayer() );
}
CEntity* CStanceUtils::chooseTarget( float x, float z, float radius, CPlayer* myPlayer )
{
std::vector<CEntity*> results;
g_EntityManager.GetInRange( x, z, radius, results );
float bestDist = 1e20f;
CEntity* bestTarget = 0;
for( size_t i=0; i<results.size(); i++ )
{
CEntity* ent = results[i];
if( myPlayer->GetDiplomaticStance( ent->GetPlayer() ) == DIPLOMACY_ENEMY )
{
float dist = ent->distance2D( x, z );
if( dist < bestDist )
{
bestDist = dist;
bestTarget = ent;
}
}
}
return bestTarget;
}