2011-02-10 17:06:28 +01:00
/* Copyright (C) 2011 Wildfire Games.
2010-01-29 22:13:18 +01:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "precompiled.h"
# include "simulation2/system/Component.h"
# include "ICmpObstruction.h"
2010-03-18 00:13:21 +01:00
# include "ICmpObstructionManager.h"
2010-04-30 01:36:05 +02:00
# include "ICmpPosition.h"
2010-01-29 22:13:18 +01:00
# include "simulation2/MessageTypes.h"
/**
* Obstruction implementation . This keeps the ICmpPathfinder ' s model of the world updated when the
* entities move and die , with shapes derived from ICmpFootprint .
*/
class CCmpObstruction : public ICmpObstruction
{
public :
static void ClassInit ( CComponentManager & componentManager )
{
componentManager . SubscribeToMessageType ( MT_PositionChanged ) ;
componentManager . SubscribeToMessageType ( MT_Destroy ) ;
}
DEFAULT_COMPONENT_ALLOCATOR ( Obstruction )
2010-04-30 01:36:05 +02:00
// Template state:
enum {
STATIC ,
UNIT
} m_Type ;
entity_pos_t m_Size0 ; // radius or width
entity_pos_t m_Size1 ; // radius or depth
2011-02-24 22:49:24 +01:00
u8 m_TemplateFlags ;
2010-03-18 00:13:21 +01:00
2010-04-30 01:36:05 +02:00
// Dynamic state:
2011-02-10 17:06:28 +01:00
bool m_Active ; // whether the obstruction is obstructing or just an inactive placeholder
2010-04-30 01:36:05 +02:00
bool m_Moving ;
2010-09-03 11:55:14 +02:00
entity_id_t m_ControlGroup ;
2010-03-18 00:13:21 +01:00
ICmpObstructionManager : : tag_t m_Tag ;
2011-02-24 22:49:24 +01:00
u8 m_Flags ;
2010-03-18 00:13:21 +01:00
2010-04-09 21:02:39 +02:00
static std : : string GetSchema ( )
{
return
2010-04-23 18:09:03 +02:00
" <a:example/> "
2011-02-10 17:06:28 +01:00
" <a:help>Causes this entity to obstruct the motion of other units.</a:help> "
2010-04-30 01:36:05 +02:00
" <choice> "
" <element name='Static'> "
" <attribute name='width'> "
" <ref name='positiveDecimal'/> "
" </attribute> "
" <attribute name='depth'> "
" <ref name='positiveDecimal'/> "
" </attribute> "
" </element> "
" <element name='Unit'> "
" <attribute name='radius'> "
" <ref name='positiveDecimal'/> "
" </attribute> "
" </element> "
" </choice> "
2011-02-10 17:06:28 +01:00
" <element name='Active' a:help='If false, this entity will be ignored in collision tests by other units but can still perform its own collision tests'> "
" <data type='boolean'/> "
" </element> "
" <element name='BlockMovement' a:help='Whether units should be allowed to walk through this entity'> "
" <data type='boolean'/> "
" </element> "
" <element name='BlockPathfinding' a:help='Whether the long-distance pathfinder should avoid paths through this entity. This should only be set for large stationary obstructions'> "
" <data type='boolean'/> "
" </element> "
" <element name='BlockFoundation' a:help='Whether players should be unable to place building foundations on top of this entity. If true, BlockConstruction should be true too'> "
" <data type='boolean'/> "
" </element> "
" <element name='BlockConstruction' a:help='Whether players should be unable to begin constructing buildings placed on top of this entity'> "
" <data type='boolean'/> "
2011-02-24 22:49:24 +01:00
" </element> "
" <element name='DisableBlockMovement' a:help='If true, BlockMovement will be overridden and treated as false. (This is a special case to handle foundations)'> "
" <data type='boolean'/> "
" </element> "
" <element name='DisableBlockPathfinding' a:help='If true, BlockPathfinding will be overridden and treated as false. (This is a special case to handle foundations)'> "
" <data type='boolean'/> "
2011-02-10 17:06:28 +01:00
" </element> " ;
2010-04-09 21:02:39 +02:00
}
2010-04-23 18:09:03 +02:00
2011-01-16 15:08:38 +01:00
virtual void Init ( const CParamNode & paramNode )
2010-01-29 22:13:18 +01:00
{
2010-04-30 01:36:05 +02:00
if ( paramNode . GetChild ( " Unit " ) . IsOk ( ) )
{
m_Type = UNIT ;
m_Size0 = m_Size1 = paramNode . GetChild ( " Unit " ) . GetChild ( " @radius " ) . ToFixed ( ) ;
}
else
{
m_Type = STATIC ;
m_Size0 = paramNode . GetChild ( " Static " ) . GetChild ( " @width " ) . ToFixed ( ) ;
m_Size1 = paramNode . GetChild ( " Static " ) . GetChild ( " @depth " ) . ToFixed ( ) ;
}
2011-02-24 22:49:24 +01:00
m_TemplateFlags = 0 ;
2011-02-10 17:06:28 +01:00
if ( paramNode . GetChild ( " BlockMovement " ) . ToBool ( ) )
2011-02-24 22:49:24 +01:00
m_TemplateFlags | = ICmpObstructionManager : : FLAG_BLOCK_MOVEMENT ;
2011-02-10 17:06:28 +01:00
if ( paramNode . GetChild ( " BlockPathfinding " ) . ToBool ( ) )
2011-02-24 22:49:24 +01:00
m_TemplateFlags | = ICmpObstructionManager : : FLAG_BLOCK_PATHFINDING ;
2011-02-10 17:06:28 +01:00
if ( paramNode . GetChild ( " BlockFoundation " ) . ToBool ( ) )
2011-02-24 22:49:24 +01:00
m_TemplateFlags | = ICmpObstructionManager : : FLAG_BLOCK_FOUNDATION ;
2011-02-10 17:06:28 +01:00
if ( paramNode . GetChild ( " BlockConstruction " ) . ToBool ( ) )
2011-02-24 22:49:24 +01:00
m_TemplateFlags | = ICmpObstructionManager : : FLAG_BLOCK_CONSTRUCTION ;
m_Flags = m_TemplateFlags ;
if ( paramNode . GetChild ( " DisableBlockMovement " ) . ToBool ( ) )
m_Flags & = ~ ICmpObstructionManager : : FLAG_BLOCK_MOVEMENT ;
if ( paramNode . GetChild ( " DisableBlockPathfinding " ) . ToBool ( ) )
m_Flags & = ~ ICmpObstructionManager : : FLAG_BLOCK_PATHFINDING ;
2011-02-10 17:06:28 +01:00
m_Active = paramNode . GetChild ( " Active " ) . ToBool ( ) ;
2010-03-18 00:13:21 +01:00
2010-07-04 19:19:38 +02:00
m_Tag = ICmpObstructionManager : : tag_t ( ) ;
2010-04-30 01:36:05 +02:00
m_Moving = false ;
2010-09-03 11:55:14 +02:00
m_ControlGroup = GetEntityId ( ) ;
2010-01-29 22:13:18 +01:00
}
2011-01-16 15:08:38 +01:00
virtual void Deinit ( )
2010-01-29 22:13:18 +01:00
{
}
2010-10-23 21:59:40 +02:00
template < typename S >
void SerializeCommon ( S & serialize )
2010-01-29 22:13:18 +01:00
{
2011-02-10 17:06:28 +01:00
serialize . Bool ( " active " , m_Active ) ;
2010-10-23 21:59:40 +02:00
serialize . Bool ( " moving " , m_Moving ) ;
serialize . NumberU32_Unbounded ( " control group " , m_ControlGroup ) ;
serialize . NumberU32_Unbounded ( " tag " , m_Tag . n ) ;
2011-02-24 22:49:24 +01:00
serialize . NumberU8_Unbounded ( " flags " , m_Flags ) ;
2010-01-29 22:13:18 +01:00
}
2010-10-23 21:59:40 +02:00
virtual void Serialize ( ISerializer & serialize )
{
SerializeCommon ( serialize ) ;
}
2011-01-16 15:08:38 +01:00
virtual void Deserialize ( const CParamNode & paramNode , IDeserializer & deserialize )
2010-01-29 22:13:18 +01:00
{
2011-01-16 15:08:38 +01:00
Init ( paramNode ) ;
2010-01-29 22:13:18 +01:00
2010-10-23 21:59:40 +02:00
SerializeCommon ( deserialize ) ;
2010-01-29 22:13:18 +01:00
}
2011-01-16 15:08:38 +01:00
virtual void HandleMessage ( const CMessage & msg , bool UNUSED ( global ) )
2010-01-29 22:13:18 +01:00
{
switch ( msg . GetType ( ) )
{
case MT_PositionChanged :
{
2010-03-18 00:13:21 +01:00
if ( ! m_Active )
break ;
2010-01-29 22:13:18 +01:00
const CMessagePositionChanged & data = static_cast < const CMessagePositionChanged & > ( msg ) ;
2010-07-04 19:19:38 +02:00
if ( ! data . inWorld & & ! m_Tag . valid ( ) )
2010-03-18 00:13:21 +01:00
break ; // nothing needs to change
2010-01-29 22:13:18 +01:00
2011-01-16 15:08:38 +01:00
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
2010-03-18 00:13:21 +01:00
if ( cmpObstructionManager . null ( ) )
2011-02-10 17:06:28 +01:00
break ; // error
2010-01-29 22:13:18 +01:00
2010-07-04 19:19:38 +02:00
if ( data . inWorld & & m_Tag . valid ( ) )
2010-01-29 22:13:18 +01:00
{
2010-03-18 00:13:21 +01:00
cmpObstructionManager - > MoveShape ( m_Tag , data . x , data . z , data . a ) ;
2010-01-29 22:13:18 +01:00
}
2010-07-04 19:19:38 +02:00
else if ( data . inWorld & & ! m_Tag . valid ( ) )
2010-01-29 22:13:18 +01:00
{
// Need to create a new pathfinder shape:
2010-04-30 01:36:05 +02:00
if ( m_Type = = STATIC )
2011-02-10 17:06:28 +01:00
m_Tag = cmpObstructionManager - > AddStaticShape ( GetEntityId ( ) ,
data . x , data . z , data . a , m_Size0 , m_Size1 , m_Flags ) ;
2010-01-29 22:13:18 +01:00
else
2011-02-10 17:06:28 +01:00
m_Tag = cmpObstructionManager - > AddUnitShape ( GetEntityId ( ) ,
data . x , data . z , m_Size0 , m_Flags | ( m_Moving ? ICmpObstructionManager : : FLAG_MOVING : 0 ) , m_ControlGroup ) ;
2010-01-29 22:13:18 +01:00
}
2010-07-04 19:19:38 +02:00
else if ( ! data . inWorld & & m_Tag . valid ( ) )
2010-01-29 22:13:18 +01:00
{
2010-03-18 00:13:21 +01:00
cmpObstructionManager - > RemoveShape ( m_Tag ) ;
2010-07-04 19:19:38 +02:00
m_Tag = ICmpObstructionManager : : tag_t ( ) ;
2010-01-29 22:13:18 +01:00
}
break ;
}
case MT_Destroy :
{
2010-07-04 19:19:38 +02:00
if ( m_Tag . valid ( ) )
2010-01-29 22:13:18 +01:00
{
2011-01-16 15:08:38 +01:00
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
2010-03-18 00:13:21 +01:00
if ( cmpObstructionManager . null ( ) )
2011-02-10 17:06:28 +01:00
break ; // error
2010-01-29 22:13:18 +01:00
2010-03-18 00:13:21 +01:00
cmpObstructionManager - > RemoveShape ( m_Tag ) ;
2010-07-04 19:19:38 +02:00
m_Tag = ICmpObstructionManager : : tag_t ( ) ;
2010-01-29 22:13:18 +01:00
}
break ;
}
}
}
2010-03-18 00:13:21 +01:00
2011-02-10 17:06:28 +01:00
virtual void SetActive ( bool active )
{
if ( active & & ! m_Active )
{
m_Active = true ;
// Construct the obstruction shape
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( cmpObstructionManager . null ( ) )
return ; // error
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , GetEntityId ( ) ) ;
if ( cmpPosition . null ( ) )
return ; // error
if ( ! cmpPosition - > IsInWorld ( ) )
return ; // don't need an obstruction
CFixedVector2D pos = cmpPosition - > GetPosition2D ( ) ;
if ( m_Type = = STATIC )
m_Tag = cmpObstructionManager - > AddStaticShape ( GetEntityId ( ) ,
pos . X , pos . Y , cmpPosition - > GetRotation ( ) . Y , m_Size0 , m_Size1 , m_Flags ) ;
else
m_Tag = cmpObstructionManager - > AddUnitShape ( GetEntityId ( ) ,
pos . X , pos . Y , m_Size0 , m_Flags | ( m_Moving ? ICmpObstructionManager : : FLAG_MOVING : 0 ) , m_ControlGroup ) ;
}
else if ( ! active & & m_Active )
{
m_Active = false ;
// Delete the obstruction shape
if ( m_Tag . valid ( ) )
{
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( cmpObstructionManager . null ( ) )
return ; // error
cmpObstructionManager - > RemoveShape ( m_Tag ) ;
m_Tag = ICmpObstructionManager : : tag_t ( ) ;
}
}
// else we didn't change the active status
}
2011-02-24 22:49:24 +01:00
virtual void SetDisableBlockMovementPathfinding ( bool disabled )
{
if ( disabled )
{
// Remove the blocking flags
m_Flags & = ~ ICmpObstructionManager : : FLAG_BLOCK_MOVEMENT ;
m_Flags & = ~ ICmpObstructionManager : : FLAG_BLOCK_PATHFINDING ;
}
else
{
// Add the blocking flags if the template had enabled them
m_Flags | = ( m_TemplateFlags & ICmpObstructionManager : : FLAG_BLOCK_MOVEMENT ) ;
m_Flags | = ( m_TemplateFlags & ICmpObstructionManager : : FLAG_BLOCK_PATHFINDING ) ;
}
// Reset the shape with the new flags (kind of inefficiently - we
// should have a ICmpObstructionManager::SetFlags function or something)
if ( m_Active )
{
SetActive ( false ) ;
SetActive ( true ) ;
}
}
2011-05-13 22:32:41 +02:00
virtual bool GetBlockMovementFlag ( )
{
return ( m_TemplateFlags & ICmpObstructionManager : : FLAG_BLOCK_MOVEMENT ) ! = 0 ;
}
2010-04-30 01:36:05 +02:00
virtual ICmpObstructionManager : : tag_t GetObstruction ( )
2010-03-18 00:13:21 +01:00
{
2010-04-30 01:36:05 +02:00
return m_Tag ;
}
2010-03-18 00:13:21 +01:00
2011-02-10 17:06:28 +01:00
virtual bool GetObstructionSquare ( ICmpObstructionManager : : ObstructionSquare & out )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , GetEntityId ( ) ) ;
if ( cmpPosition . null ( ) )
return false ; // error
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( cmpObstructionManager . null ( ) )
return false ; // error
if ( ! cmpPosition - > IsInWorld ( ) )
return false ; // no obstruction square
CFixedVector2D pos = cmpPosition - > GetPosition2D ( ) ;
if ( m_Type = = STATIC )
out = cmpObstructionManager - > GetStaticShapeObstruction ( pos . X , pos . Y , cmpPosition - > GetRotation ( ) . Y , m_Size0 , m_Size1 ) ;
else
out = cmpObstructionManager - > GetUnitShapeObstruction ( pos . X , pos . Y , m_Size0 ) ;
return true ;
}
2010-04-30 01:36:05 +02:00
virtual entity_pos_t GetUnitRadius ( )
{
if ( m_Type = = UNIT )
return m_Size0 ;
else
return entity_pos_t : : Zero ( ) ;
}
2011-02-10 17:06:28 +01:00
virtual bool CheckFoundationCollisions ( )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , GetEntityId ( ) ) ;
if ( cmpPosition . null ( ) )
return false ; // error
if ( ! cmpPosition - > IsInWorld ( ) )
return false ; // no obstruction
CFixedVector2D pos = cmpPosition - > GetPosition2D ( ) ;
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( cmpObstructionManager . null ( ) )
return false ; // error
// Ignore collisions with self, or with non-foundation-blocking obstructions
SkipTagFlagsObstructionFilter filter ( m_Tag , ICmpObstructionManager : : FLAG_BLOCK_FOUNDATION ) ;
if ( m_Type = = STATIC )
return cmpObstructionManager - > TestStaticShape ( filter , pos . X , pos . Y , cmpPosition - > GetRotation ( ) . Y , m_Size0 , m_Size1 , NULL ) ;
else
return cmpObstructionManager - > TestUnitShape ( filter , pos . X , pos . Y , m_Size0 , NULL ) ;
}
virtual std : : vector < entity_id_t > GetConstructionCollisions ( )
2010-04-30 01:36:05 +02:00
{
2011-02-10 17:06:28 +01:00
std : : vector < entity_id_t > ret ;
2010-05-01 11:48:39 +02:00
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , GetEntityId ( ) ) ;
2010-03-18 00:13:21 +01:00
if ( cmpPosition . null ( ) )
2011-02-10 17:06:28 +01:00
return ret ; // error
if ( ! cmpPosition - > IsInWorld ( ) )
return ret ; // no obstruction
2010-03-18 00:13:21 +01:00
2010-07-29 22:39:23 +02:00
CFixedVector2D pos = cmpPosition - > GetPosition2D ( ) ;
2010-03-18 00:13:21 +01:00
2010-05-01 11:48:39 +02:00
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
2011-02-10 17:06:28 +01:00
if ( cmpObstructionManager . null ( ) )
return ret ; // error
2010-03-18 00:13:21 +01:00
2011-02-10 17:06:28 +01:00
// Ignore collisions with self, or with non-construction-blocking obstructions
SkipTagFlagsObstructionFilter filter ( m_Tag , ICmpObstructionManager : : FLAG_BLOCK_CONSTRUCTION ) ;
2010-03-18 00:13:21 +01:00
2010-04-30 01:36:05 +02:00
if ( m_Type = = STATIC )
2011-02-10 17:06:28 +01:00
cmpObstructionManager - > TestStaticShape ( filter , pos . X , pos . Y , cmpPosition - > GetRotation ( ) . Y , m_Size0 , m_Size1 , & ret ) ;
2010-03-18 00:13:21 +01:00
else
2011-02-10 17:06:28 +01:00
cmpObstructionManager - > TestUnitShape ( filter , pos . X , pos . Y , m_Size0 , & ret ) ;
return ret ;
2010-03-18 00:13:21 +01:00
}
2010-09-03 11:55:14 +02:00
virtual void SetMovingFlag ( bool enabled )
{
m_Moving = enabled ;
if ( m_Tag . valid ( ) & & m_Type = = UNIT )
{
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( ! cmpObstructionManager . null ( ) )
cmpObstructionManager - > SetUnitMovingFlag ( m_Tag , m_Moving ) ;
}
}
virtual void SetControlGroup ( entity_id_t group )
{
m_ControlGroup = group ;
if ( m_Tag . valid ( ) & & m_Type = = UNIT )
{
CmpPtr < ICmpObstructionManager > cmpObstructionManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( ! cmpObstructionManager . null ( ) )
cmpObstructionManager - > SetUnitControlGroup ( m_Tag , m_ControlGroup ) ;
}
}
2010-01-29 22:13:18 +01:00
} ;
REGISTER_COMPONENT_TYPE ( Obstruction )