mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-23 15:53:16 +00:00
Combat code, some scripting, broken network, and fixed some bugs.
This was SVN commit r1301.
This commit is contained in:
@@ -11,7 +11,21 @@
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_millis )
|
||||
enum EGotoSituation
|
||||
{
|
||||
NORMAL = 0,
|
||||
ALREADY_AT_DESTINATION,
|
||||
REACHED_DESTINATION,
|
||||
COLLISION_WITH_DESTINATION,
|
||||
COLLISION_NEAR_DESTINATION,
|
||||
COLLISION_OVERLAPPING_OBJECTS,
|
||||
COLLISION_OTHER,
|
||||
WOULD_LEAVE_MAP
|
||||
};
|
||||
|
||||
// Does all the shared processing for line-of-sight gotos
|
||||
|
||||
EGotoSituation CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, HEntity& collide )
|
||||
{
|
||||
float timestep=timestep_millis/1000.0f;
|
||||
|
||||
@@ -22,15 +36,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
float len = delta.length();
|
||||
|
||||
if( len < 0.1f )
|
||||
{
|
||||
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
|
||||
{
|
||||
repath();
|
||||
}
|
||||
else
|
||||
m_orderQueue.pop_front();
|
||||
return( false );
|
||||
}
|
||||
return( ALREADY_AT_DESTINATION );
|
||||
|
||||
// Curve smoothing.
|
||||
// Here there be trig.
|
||||
@@ -74,8 +80,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
// place until we're looking the right way. At least, that's what
|
||||
// seems logical. But in most cases that looks worse. So actually,
|
||||
// let's not.
|
||||
|
||||
|
||||
|
||||
if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED )
|
||||
m_orientation = m_targetorientation;
|
||||
|
||||
@@ -86,13 +91,16 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
m_orientation = m_targetorientation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
|
||||
((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
|
||||
|
||||
EGotoSituation rc = NORMAL;
|
||||
|
||||
if( scale > len )
|
||||
{
|
||||
scale = len;
|
||||
rc = REACHED_DESTINATION;
|
||||
}
|
||||
|
||||
delta = m_ahead * scale;
|
||||
|
||||
@@ -103,7 +111,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
|
||||
m_bounds->setPosition( m_position.X, m_position.Z );
|
||||
|
||||
HEntity collide = getCollisionObject( this );
|
||||
collide = getCollisionObject( this );
|
||||
|
||||
if( collide )
|
||||
{
|
||||
@@ -123,17 +131,13 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
|
||||
// Erm... do nothing?
|
||||
|
||||
return( false );
|
||||
return( COLLISION_OVERLAPPING_OBJECTS );
|
||||
}
|
||||
|
||||
// No. Is our destination within the obstacle?
|
||||
if( collide->m_bounds->contains( current->m_data[0].location ) )
|
||||
{
|
||||
// Yes? All well and good, then. Stop here.
|
||||
m_orderQueue.pop_front();
|
||||
return( false );
|
||||
}
|
||||
|
||||
return( COLLISION_WITH_DESTINATION );
|
||||
|
||||
// No. Are we nearing our destination, do we wish to stop there, and is it obstructed?
|
||||
|
||||
if( ( m_orderQueue.size() == 1 ) && ( len <= 10.0f ) )
|
||||
@@ -142,30 +146,84 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
if( getCollisionObject( &destinationObs ) )
|
||||
{
|
||||
// Yes. (Chances are a bunch of units were tasked to the same destination)
|
||||
// Here's a wierd idea: (I hope it works)
|
||||
// Spiral round the destination until a free point is found.
|
||||
float interval = destinationObs.m_radius;
|
||||
float r = interval, theta = 0.0f, delta;
|
||||
float _x = current->m_data[0].location.x, _y = current->m_data[0].location.y;
|
||||
|
||||
while( true )
|
||||
{
|
||||
delta = interval / r;
|
||||
theta += delta;
|
||||
r += ( interval * delta ) / ( 2 * PI );
|
||||
destinationObs.setPosition( _x + r * cosf( theta ), _y + r * sinf( theta ) );
|
||||
if( !getCollisionObject( &destinationObs ) ) break;
|
||||
}
|
||||
|
||||
// Reset our destination
|
||||
current->m_data[0].location.x = _x + r * cosf( theta );
|
||||
current->m_data[0].location.y = _y + r * sinf( theta );
|
||||
|
||||
return( false );
|
||||
return( COLLISION_NEAR_DESTINATION );
|
||||
}
|
||||
}
|
||||
|
||||
// No? Path around it.
|
||||
// No?
|
||||
return( COLLISION_OTHER );
|
||||
|
||||
}
|
||||
|
||||
// Will we step off the map?
|
||||
if( !g_Game->GetWorld()->GetTerrain()->isOnMap( m_position.X, m_position.Z ) )
|
||||
{
|
||||
// Yes. That's not a particularly good idea, either.
|
||||
|
||||
m_position.X -= delta.x;
|
||||
m_position.Z -= delta.y;
|
||||
m_bounds->setPosition( m_position.X, m_position.Z );
|
||||
|
||||
// All things being equal, we should only get here while on a collision path
|
||||
// (No destination should be off the map)
|
||||
|
||||
return( WOULD_LEAVE_MAP );
|
||||
}
|
||||
|
||||
// No. I suppose it's OK to go there, then. *disappointed*
|
||||
|
||||
return( rc );
|
||||
}
|
||||
|
||||
|
||||
bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_millis )
|
||||
{
|
||||
HEntity collide;
|
||||
switch( processGotoHelper( current, timestep_millis, collide ) )
|
||||
{
|
||||
case ALREADY_AT_DESTINATION:
|
||||
// If on a collision path; decide where to go next. Otherwise, proceed to the next waypoint.
|
||||
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
|
||||
{
|
||||
repath();
|
||||
}
|
||||
else
|
||||
m_orderQueue.pop_front();
|
||||
return( false );
|
||||
case COLLISION_OVERLAPPING_OBJECTS:
|
||||
return( false );
|
||||
case COLLISION_WITH_DESTINATION:
|
||||
// We're here...
|
||||
m_orderQueue.pop_front();
|
||||
return( false );
|
||||
case COLLISION_NEAR_DESTINATION:
|
||||
{
|
||||
// Here's a wierd idea: (I hope it works)
|
||||
// Spiral round the destination until a free point is found.
|
||||
CBoundingCircle destinationObs( current->m_data[0].location.x, current->m_data[0].location.y, m_bounds->m_radius );
|
||||
|
||||
float interval = destinationObs.m_radius;
|
||||
float r = interval, theta = 0.0f, delta;
|
||||
float _x = current->m_data[0].location.x, _y = current->m_data[0].location.y;
|
||||
|
||||
while( true )
|
||||
{
|
||||
delta = interval / r;
|
||||
theta += delta;
|
||||
r += ( interval * delta ) / ( 2 * PI );
|
||||
destinationObs.setPosition( _x + r * cosf( theta ), _y + r * sinf( theta ) );
|
||||
if( !getCollisionObject( &destinationObs ) ) break;
|
||||
}
|
||||
|
||||
// Reset our destination
|
||||
current->m_data[0].location.x = _x + r * cosf( theta );
|
||||
current->m_data[0].location.y = _y + r * sinf( theta );
|
||||
|
||||
return( false );
|
||||
}
|
||||
case COLLISION_OTHER:
|
||||
{
|
||||
// Path around it.
|
||||
|
||||
CEntityOrder avoidance;
|
||||
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
|
||||
@@ -194,27 +252,145 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
m_orderQueue.pop_front();
|
||||
m_orderQueue.push_front( avoidance );
|
||||
return( false );
|
||||
|
||||
}
|
||||
|
||||
// Will we step off the map?
|
||||
if( !g_Game->GetWorld()->GetTerrain()->isOnMap( m_position.X, m_position.Z ) )
|
||||
{
|
||||
// Yes. That's not a particularly good idea, either.
|
||||
|
||||
m_position.X -= delta.x;
|
||||
m_position.Z -= delta.y;
|
||||
m_bounds->setPosition( m_position.X, m_position.Z );
|
||||
|
||||
// All things being equal, we should only get here while on a collision path
|
||||
// (No destination should be off the map)
|
||||
|
||||
case WOULD_LEAVE_MAP:
|
||||
// Just stop here, repath if necessary.
|
||||
|
||||
m_orderQueue.pop_front();
|
||||
return( false );
|
||||
default:
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
|
||||
bool CEntity::processAttackMelee( CEntityOrder* current, size_t timestep_millis )
|
||||
{
|
||||
m_orderQueue.pop_front();
|
||||
|
||||
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
|
||||
return( false );
|
||||
|
||||
current->m_data[0].location = current->m_data[0].entity->m_position;
|
||||
|
||||
if( ( current->m_data[0].location - m_position ).length() < m_meleeRange )
|
||||
{
|
||||
current->m_type = CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING;
|
||||
return( true );
|
||||
}
|
||||
|
||||
// No. I suppose it's OK to go there, then. *disappointed*
|
||||
if( m_transition )
|
||||
{
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
}
|
||||
|
||||
// The pathfinder will push its result back into this unit's queue.
|
||||
|
||||
g_Pathfinder.requestMeleeAttackPath( me, current->m_data[0].entity );
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool CEntity::processAttackMeleeNoPathing( CEntityOrder* current, size_t timestep_millis )
|
||||
{
|
||||
// Target's dead? Then our work here is done.
|
||||
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
|
||||
{
|
||||
m_orderQueue.pop_front();
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Still playing attack animation? Suspend processing.
|
||||
if( m_actor->GetModel()->GetAnimation() == m_actor->GetObject()->m_MeleeAnim )
|
||||
return( false );
|
||||
|
||||
// Just transitioned? No animation? (=> melee just finished) Play walk.
|
||||
if( m_transition || !m_actor->GetModel()->GetAnimation() )
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
|
||||
CVector2D delta = current->m_data[0].entity->m_position - m_position;
|
||||
|
||||
float adjRange = m_meleeRange + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
|
||||
float adjMinRange = m_meleeRangeMin + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
|
||||
|
||||
if( delta.within( adjMinRange ) )
|
||||
{
|
||||
// Too close... do nothing.
|
||||
return( false );
|
||||
}
|
||||
|
||||
if( !delta.within( adjRange ) )
|
||||
{
|
||||
// Too far away at the moment, chase after the target...
|
||||
// We're aiming to end up at a location just inside our maximum range
|
||||
// (is this good enough?)
|
||||
|
||||
delta = delta.normalize() * ( adjRange - m_bounds->m_radius );
|
||||
|
||||
current->m_data[0].location = (CVector2D)current->m_data[0].entity->m_position - delta;
|
||||
|
||||
HEntity collide;
|
||||
switch( processGotoHelper( current, timestep_millis, collide ) )
|
||||
{
|
||||
case ALREADY_AT_DESTINATION:
|
||||
case REACHED_DESTINATION:
|
||||
case COLLISION_WITH_DESTINATION:
|
||||
// Not too far any more...
|
||||
break;
|
||||
case NORMAL:
|
||||
// May or may not be close enough, check...
|
||||
// (Assuming the delta above will never take us within minimum range)
|
||||
delta = current->m_data[0].entity->m_position - m_position;
|
||||
if( delta.within( adjRange ) )
|
||||
break;
|
||||
// Otherwise, continue chasing
|
||||
return( false );
|
||||
default:
|
||||
// Path around it.
|
||||
|
||||
CEntityOrder avoidance;
|
||||
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
|
||||
CVector2D right;
|
||||
right.x = m_ahead.y; right.y = -m_ahead.x;
|
||||
CVector2D avoidancePosition;
|
||||
|
||||
// Which is the shortest diversion, going left or right?
|
||||
// (Weight a little towards the right, to stop both units dodging the same way)
|
||||
|
||||
if( ( collide->m_bounds->m_pos - m_bounds->m_pos ).dot( right ) < 1 )
|
||||
{
|
||||
// Turn right.
|
||||
avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Turn left.
|
||||
avoidancePosition = collide->m_bounds->m_pos - right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
|
||||
}
|
||||
|
||||
// Create a short path representing this detour
|
||||
|
||||
avoidance.m_data[0].location = avoidancePosition;
|
||||
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
|
||||
m_orderQueue.pop_front();
|
||||
m_orderQueue.push_front( avoidance );
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close enough, but turn to face them.
|
||||
m_orientation = atan2( delta.x, delta.y );
|
||||
m_ahead = delta.normalize();
|
||||
}
|
||||
|
||||
// Now we've got this far:
|
||||
// Pointy end goes into the other man...
|
||||
|
||||
CEventAttack AttackEvent( current->m_data[0].entity );
|
||||
|
||||
if( DispatchEvent( &AttackEvent ) )
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_MeleeAnim, true );
|
||||
|
||||
return( false );
|
||||
}
|
||||
@@ -231,11 +407,11 @@ bool CEntity::processGoto( CEntityOrder* current, size_t timestep_millis )
|
||||
if( ( path_to - pos ).length() < 0.1f )
|
||||
return( false );
|
||||
|
||||
if( !m_moving )
|
||||
if( m_transition )
|
||||
{
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
m_moving = true;
|
||||
}
|
||||
|
||||
// The pathfinder will push its result back into this unit's queue.
|
||||
|
||||
Reference in New Issue
Block a user