2025-04-10 21:24:32 -07:00
/* Copyright (C) 2025 Wildfire Games.
2023-12-02 16:30:12 -08:00
* This file is part of 0 A . D .
2010-01-09 11:20:14 -08:00
*
2023-12-02 16:30:12 -08:00
* 0 A . D . is free software : you can redistribute it and / or modify
2010-01-09 11:20:14 -08:00
* 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 .
*
2023-12-02 16:30:12 -08:00
* 0 A . D . is distributed in the hope that it will be useful ,
2010-01-09 11:20:14 -08:00
* 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
2023-12-02 16:30:12 -08:00
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
2010-01-09 11:20:14 -08:00
*/
# include "precompiled.h"
# include "simulation2/system/Component.h"
# include "ICmpPosition.h"
2010-01-29 13:13:18 -08:00
# include "simulation2/MessageTypes.h"
2021-04-04 22:22:25 -07:00
# include "simulation2/serialization/SerializedTypes.h"
2010-01-29 13:13:18 -08:00
2010-01-09 11:20:14 -08:00
# include "ICmpTerrain.h"
2014-06-01 11:24:50 -07:00
# include "ICmpVisual.h"
2010-05-27 16:23:53 -07:00
# include "ICmpWaterManager.h"
2010-01-09 11:20:14 -08:00
# include "graphics/Terrain.h"
# include "lib/rand.h"
# include "maths/MathUtil.h"
# include "maths/Matrix3D.h"
# include "maths/Vector3D.h"
2013-07-15 20:46:30 -07:00
# include "maths/Vector2D.h"
2010-01-09 11:20:14 -08:00
# include "ps/CLogger.h"
2016-06-21 03:33:11 -07:00
# include "ps/Profile.h"
2010-01-09 11:20:14 -08:00
/**
* Basic ICmpPosition implementation .
*/
2022-03-03 14:42:26 -08:00
class CCmpPosition final : public ICmpPosition
2010-01-09 11:20:14 -08:00
{
public :
static void ClassInit ( CComponentManager & componentManager )
{
2010-01-22 12:03:14 -08:00
componentManager . SubscribeToMessageType ( MT_TurnStart ) ;
2014-06-01 11:24:50 -07:00
componentManager . SubscribeToMessageType ( MT_TerrainChanged ) ;
componentManager . SubscribeToMessageType ( MT_WaterChanged ) ;
componentManager . SubscribeToMessageType ( MT_Deserialized ) ;
2010-01-09 11:20:14 -08:00
// TODO: if this component turns out to be a performance issue, it should
// be optimised by creating a new PositionStatic component that doesn't subscribe
// to messages and doesn't store LastX/LastZ, and that should be used for all
// entities that don't move
}
DEFAULT_COMPONENT_ALLOCATOR ( Position )
// Template state:
2025-01-23 12:47:26 -08:00
enum class AnchorType
2010-01-09 11:20:14 -08:00
{
2025-01-23 12:47:26 -08:00
UNDEFINED = - 1 , // Used for m_ActorAnchorType only since it's optional.
2010-01-09 11:20:14 -08:00
UPRIGHT = 0 ,
PITCH = 1 ,
PITCH_ROLL = 2 ,
2014-06-01 11:24:50 -07:00
ROLL = 3 ,
2025-01-23 12:47:26 -08:00
} ;
AnchorType m_AnchorType ;
2010-01-09 11:20:14 -08:00
bool m_Floating ;
2017-09-17 20:55:33 -07:00
entity_pos_t m_FloatDepth ;
2020-12-17 14:54:14 -08:00
// Maximum radians per second, used by InterpolatedRotY to follow RotY and the unitMotion.
fixed m_RotYSpeed ;
2010-01-09 11:20:14 -08:00
// Dynamic state:
bool m_InWorld ;
2012-05-19 16:07:41 -07:00
// m_LastX/Z contain the position from the start of the most recent turn
// m_PrevX/Z conatain the position from the turn before that
entity_pos_t m_X , m_Z , m_LastX , m_LastZ , m_PrevX , m_PrevZ ; // these values contain undefined junk if !InWorld
2014-05-26 09:23:46 -07:00
entity_pos_t m_Y , m_LastYDifference ; // either the relative or the absolute Y coordinate
bool m_RelativeToGround ; // whether m_Y is relative to terrain/water plane, or an absolute height
2010-01-09 11:20:14 -08:00
2014-06-01 11:24:50 -07:00
fixed m_ConstructionProgress ;
2014-05-30 07:46:06 -07:00
// when the entity is a turret, only m_RotY is used, and this is the rotation
// relative to the parent entity
2013-03-23 10:59:54 -07:00
entity_angle_t m_RotX , m_RotY , m_RotZ ;
2013-07-15 20:46:30 -07:00
2014-05-30 07:46:06 -07:00
entity_id_t m_TurretParent ;
CFixedVector3D m_TurretPosition ;
std : : set < entity_id_t > m_Turrets ;
2014-06-01 11:24:50 -07:00
// Not serialized:
2025-01-23 12:47:26 -08:00
AnchorType m_ActorAnchorType ;
2013-07-15 20:46:30 -07:00
float m_InterpolatedRotX , m_InterpolatedRotY , m_InterpolatedRotZ ;
2014-06-01 11:24:50 -07:00
float m_LastInterpolatedRotX , m_LastInterpolatedRotZ ;
bool m_ActorFloating ;
2013-07-15 20:46:30 -07:00
2014-06-19 16:20:12 -07:00
bool m_EnabledMessageInterpolate ;
2010-04-09 12:02:39 -07:00
static std : : string GetSchema ( )
{
return
2010-04-23 09:09:03 -07:00
" <a:help>Allows this entity to exist at a location (and orientation) in the world, and defines some details of the positioning.</a:help> "
" <a:example> "
" <Anchor>upright</Anchor> "
" <Altitude>0.0</Altitude> "
" <Floating>false</Floating> "
2017-09-17 20:55:33 -07:00
" <FloatDepth>0.0</FloatDepth> "
2011-06-09 12:44:40 -07:00
" <TurnRate>6.0</TurnRate> "
2010-04-23 09:09:03 -07:00
" </a:example> "
2010-04-09 12:02:39 -07:00
" <element name='Anchor' a:help='Automatic rotation to follow the slope of terrain'> "
" <choice> "
2013-07-15 20:46:30 -07:00
" <value a:help='Always stand straight up (e.g. humans)'>upright</value> "
" <value a:help='Rotate backwards and forwards to follow the terrain (e.g. animals)'>pitch</value> "
" <value a:help='Rotate sideways to follow the terrain'>roll</value> "
" <value a:help='Rotate in all directions to follow the terrain (e.g. carts)'>pitch-roll</value> "
2010-04-09 12:02:39 -07:00
" </choice> "
" </element> "
2025-04-10 21:24:32 -07:00
" <element name='Altitude' a:help='Height above terrain in meters'> "
2010-04-09 12:02:39 -07:00
" <data type='decimal'/> "
" </element> "
" <element name='Floating' a:help='Whether the entity floats on water'> "
" <data type='boolean'/> "
2011-06-09 12:44:40 -07:00
" </element> "
2017-09-17 20:55:33 -07:00
" <element name='FloatDepth' a:help='The depth at which an entity floats on water (needs Floating to be true)'> "
" <ref name='nonNegativeDecimal'/> "
" </element> "
2020-12-17 14:54:14 -08:00
" <element name='TurnRate' a:help='Maximum rotation speed around Y axis, in radians per second. Used for all graphical rotations and some real unitMotion driven rotations.'> "
2011-06-09 12:44:40 -07:00
" <ref name='positiveDecimal'/> "
2010-04-09 12:02:39 -07:00
" </element> " ;
}
2022-03-07 15:04:11 -08:00
void Init ( const CParamNode & paramNode ) override
2010-01-09 11:20:14 -08:00
{
2025-01-23 12:47:26 -08:00
m_AnchorType = ParseAnchorString ( paramNode . GetChild ( " Anchor " ) . ToString ( ) ) ;
m_ActorAnchorType = AnchorType : : UNDEFINED ;
2010-01-09 11:20:14 -08:00
m_InWorld = false ;
2014-05-26 09:23:46 -07:00
m_LastYDifference = entity_pos_t : : Zero ( ) ;
m_Y = paramNode . GetChild ( " Altitude " ) . ToFixed ( ) ;
2011-06-09 12:44:40 -07:00
m_RelativeToGround = true ;
2010-02-02 15:01:17 -08:00
m_Floating = paramNode . GetChild ( " Floating " ) . ToBool ( ) ;
2017-09-17 20:55:33 -07:00
m_FloatDepth = paramNode . GetChild ( " FloatDepth " ) . ToFixed ( ) ;
2010-01-09 11:20:14 -08:00
2020-12-17 14:54:14 -08:00
m_RotYSpeed = paramNode . GetChild ( " TurnRate " ) . ToFixed ( ) ;
2010-02-10 11:28:46 -08:00
2013-03-23 10:59:54 -07:00
m_RotX = m_RotY = m_RotZ = entity_angle_t : : FromInt ( 0 ) ;
2013-07-15 20:46:30 -07:00
m_InterpolatedRotX = m_InterpolatedRotY = m_InterpolatedRotZ = 0.f ;
m_LastInterpolatedRotX = m_LastInterpolatedRotZ = 0.f ;
2014-05-30 07:46:06 -07:00
m_TurretParent = INVALID_ENTITY ;
m_TurretPosition = CFixedVector3D ( ) ;
2014-06-01 11:24:50 -07:00
m_ActorFloating = false ;
2014-06-19 16:20:12 -07:00
m_EnabledMessageInterpolate = false ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
void Deinit ( ) override
2010-01-09 11:20:14 -08:00
{
}
2022-03-07 15:04:11 -08:00
void Serialize ( ISerializer & serialize ) override
2010-01-09 11:20:14 -08:00
{
serialize . Bool ( " in world " , m_InWorld ) ;
if ( m_InWorld )
{
serialize . NumberFixed_Unbounded ( " x " , m_X ) ;
2014-05-26 09:23:46 -07:00
serialize . NumberFixed_Unbounded ( " y " , m_Y ) ;
2010-01-09 11:20:14 -08:00
serialize . NumberFixed_Unbounded ( " z " , m_Z ) ;
serialize . NumberFixed_Unbounded ( " last x " , m_LastX ) ;
2014-05-26 09:23:46 -07:00
serialize . NumberFixed_Unbounded ( " last y diff " , m_LastYDifference ) ;
2010-01-09 11:20:14 -08:00
serialize . NumberFixed_Unbounded ( " last z " , m_LastZ ) ;
}
2023-06-13 08:48:03 -07:00
2010-01-09 11:20:14 -08:00
serialize . NumberFixed_Unbounded ( " rot x " , m_RotX ) ;
serialize . NumberFixed_Unbounded ( " rot y " , m_RotY ) ;
serialize . NumberFixed_Unbounded ( " rot z " , m_RotZ ) ;
2020-12-17 14:54:14 -08:00
serialize . NumberFixed_Unbounded ( " rot y speed " , m_RotYSpeed ) ;
2014-05-26 09:23:46 -07:00
serialize . NumberFixed_Unbounded ( " altitude " , m_Y ) ;
2011-06-09 12:44:40 -07:00
serialize . Bool ( " relative " , m_RelativeToGround ) ;
2014-05-26 09:23:46 -07:00
serialize . Bool ( " floating " , m_Floating ) ;
2017-09-17 20:55:33 -07:00
serialize . NumberFixed_Unbounded ( " float depth " , m_FloatDepth ) ;
2014-06-01 11:24:50 -07:00
serialize . NumberFixed_Unbounded ( " constructionprogress " , m_ConstructionProgress ) ;
2010-01-29 13:13:18 -08:00
2010-01-09 11:20:14 -08:00
if ( serialize . IsDebug ( ) )
{
const char * anchor = " ??? " ;
switch ( m_AnchorType )
{
2025-01-23 12:47:26 -08:00
case AnchorType : : PITCH :
2016-11-23 05:02:58 -08:00
anchor = " pitch " ;
2013-07-15 20:46:30 -07:00
break ;
2025-01-23 12:47:26 -08:00
case AnchorType : : PITCH_ROLL :
2016-11-23 05:02:58 -08:00
anchor = " pitch-roll " ;
2013-07-15 20:46:30 -07:00
break ;
2016-11-23 06:09:58 -08:00
2025-01-23 12:47:26 -08:00
case AnchorType : : ROLL :
2013-07-15 20:46:30 -07:00
anchor = " roll " ;
break ;
2025-01-23 12:47:26 -08:00
case AnchorType : : UPRIGHT : // upright is the default
2016-11-23 05:02:58 -08:00
default :
anchor = " upright " ;
2013-07-15 20:46:30 -07:00
break ;
2010-01-09 11:20:14 -08:00
}
serialize . StringASCII ( " anchor " , anchor , 0 , 16 ) ;
}
2014-05-31 04:35:07 -07:00
serialize . NumberU32_Unbounded ( " turret parent " , m_TurretParent ) ;
if ( m_TurretParent ! = INVALID_ENTITY )
{
serialize . NumberFixed_Unbounded ( " x " , m_TurretPosition . X ) ;
serialize . NumberFixed_Unbounded ( " y " , m_TurretPosition . Y ) ;
serialize . NumberFixed_Unbounded ( " z " , m_TurretPosition . Z ) ;
}
2021-04-04 22:22:25 -07:00
Serializer ( serialize , " turrets " , m_Turrets ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
void Deserialize ( const CParamNode & paramNode , IDeserializer & deserialize ) override
2010-01-09 11:20:14 -08:00
{
2011-01-16 06:08:38 -08:00
Init ( paramNode ) ;
2010-01-09 11:20:14 -08:00
2010-09-17 10:53:26 -07:00
deserialize . Bool ( " in world " , m_InWorld ) ;
2010-01-09 11:20:14 -08:00
if ( m_InWorld )
{
2010-09-17 10:53:26 -07:00
deserialize . NumberFixed_Unbounded ( " x " , m_X ) ;
2014-05-26 09:23:46 -07:00
deserialize . NumberFixed_Unbounded ( " y " , m_Y ) ;
2010-09-17 10:53:26 -07:00
deserialize . NumberFixed_Unbounded ( " z " , m_Z ) ;
deserialize . NumberFixed_Unbounded ( " last x " , m_LastX ) ;
2014-05-26 09:23:46 -07:00
deserialize . NumberFixed_Unbounded ( " last y diff " , m_LastYDifference ) ;
2010-09-17 10:53:26 -07:00
deserialize . NumberFixed_Unbounded ( " last z " , m_LastZ ) ;
2010-01-09 11:20:14 -08:00
}
2023-06-13 08:48:03 -07:00
2010-09-17 10:53:26 -07:00
deserialize . NumberFixed_Unbounded ( " rot x " , m_RotX ) ;
deserialize . NumberFixed_Unbounded ( " rot y " , m_RotY ) ;
deserialize . NumberFixed_Unbounded ( " rot z " , m_RotZ ) ;
2020-12-17 14:54:14 -08:00
deserialize . NumberFixed_Unbounded ( " rot y speed " , m_RotYSpeed ) ;
2014-05-26 09:23:46 -07:00
deserialize . NumberFixed_Unbounded ( " altitude " , m_Y ) ;
2011-06-09 12:44:40 -07:00
deserialize . Bool ( " relative " , m_RelativeToGround ) ;
2014-05-26 09:23:46 -07:00
deserialize . Bool ( " floating " , m_Floating ) ;
2017-09-17 20:55:33 -07:00
deserialize . NumberFixed_Unbounded ( " float depth " , m_FloatDepth ) ;
2014-06-01 11:24:50 -07:00
deserialize . NumberFixed_Unbounded ( " constructionprogress " , m_ConstructionProgress ) ;
2010-01-09 11:20:14 -08:00
// TODO: should there be range checks on all these values?
2010-02-10 11:28:46 -08:00
m_InterpolatedRotY = m_RotY . ToFloat ( ) ;
2013-07-15 20:46:30 -07:00
2014-05-31 04:35:07 -07:00
deserialize . NumberU32_Unbounded ( " turret parent " , m_TurretParent ) ;
if ( m_TurretParent ! = INVALID_ENTITY )
{
deserialize . NumberFixed_Unbounded ( " x " , m_TurretPosition . X ) ;
deserialize . NumberFixed_Unbounded ( " y " , m_TurretPosition . Y ) ;
deserialize . NumberFixed_Unbounded ( " z " , m_TurretPosition . Z ) ;
}
2021-04-04 22:22:25 -07:00
Serializer ( deserialize , " turrets " , m_Turrets ) ;
2014-05-31 04:35:07 -07:00
2013-07-15 20:46:30 -07:00
if ( m_InWorld )
UpdateXZRotation ( ) ;
2014-06-19 16:20:12 -07:00
UpdateMessageSubscriptions ( ) ;
2010-01-09 11:20:14 -08:00
}
2014-06-01 12:06:58 -07:00
void Deserialized ( )
{
AdvertiseInterpolatedPositionChanges ( ) ;
}
2022-03-07 15:04:11 -08:00
void UpdateTurretPosition ( ) override
2014-05-30 07:46:06 -07:00
{
if ( m_TurretParent = = INVALID_ENTITY )
return ;
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( ! cmpPosition )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " Turret with parent without position component " ) ;
2014-05-30 07:46:06 -07:00
return ;
}
if ( ! cmpPosition - > IsInWorld ( ) )
MoveOutOfWorld ( ) ;
else
{
CFixedVector2D rotatedPosition = CFixedVector2D ( m_TurretPosition . X , m_TurretPosition . Z ) ;
rotatedPosition = rotatedPosition . Rotate ( cmpPosition - > GetRotation ( ) . Y ) ;
CFixedVector2D rootPosition = cmpPosition - > GetPosition2D ( ) ;
entity_pos_t x = rootPosition . X + rotatedPosition . X ;
entity_pos_t z = rootPosition . Y + rotatedPosition . Y ;
if ( ! m_InWorld | | m_X ! = x | | m_Z ! = z )
MoveTo ( x , z ) ;
entity_pos_t y = cmpPosition - > GetHeightOffset ( ) + m_TurretPosition . Y ;
if ( ! m_InWorld | | GetHeightOffset ( ) ! = y )
SetHeightOffset ( y ) ;
m_InWorld = true ;
}
}
2022-03-07 15:04:11 -08:00
std : : set < entity_id_t > * GetTurrets ( ) override
2014-05-30 07:46:06 -07:00
{
return & m_Turrets ;
}
2022-03-07 15:04:11 -08:00
void SetTurretParent ( entity_id_t id , const CFixedVector3D & offset ) override
2014-05-30 07:46:06 -07:00
{
2018-04-03 10:13:24 -07:00
entity_angle_t angle = GetRotation ( ) . Y ;
2014-05-30 07:46:06 -07:00
if ( m_TurretParent ! = INVALID_ENTITY )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( cmpPosition )
cmpPosition - > GetTurrets ( ) - > erase ( GetEntityId ( ) ) ;
}
m_TurretParent = id ;
m_TurretPosition = offset ;
if ( m_TurretParent ! = INVALID_ENTITY )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( cmpPosition )
cmpPosition - > GetTurrets ( ) - > insert ( GetEntityId ( ) ) ;
}
2018-04-03 10:13:24 -07:00
SetYRotation ( angle ) ;
2014-05-30 07:46:06 -07:00
UpdateTurretPosition ( ) ;
}
2022-03-07 15:04:11 -08:00
entity_id_t GetTurretParent ( ) const override
2014-05-31 04:35:07 -07:00
{
return m_TurretParent ;
}
2022-03-07 15:04:11 -08:00
bool IsInWorld ( ) const override
2010-01-09 11:20:14 -08:00
{
return m_InWorld ;
}
2022-03-07 15:04:11 -08:00
void MoveOutOfWorld ( ) override
2010-01-09 11:20:14 -08:00
{
m_InWorld = false ;
2010-01-29 13:13:18 -08:00
2010-04-29 16:36:05 -07:00
AdvertisePositionChanges ( ) ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
void MoveTo ( entity_pos_t x , entity_pos_t z ) override
2010-01-09 11:20:14 -08:00
{
m_X = x ;
m_Z = z ;
if ( ! m_InWorld )
{
m_InWorld = true ;
2012-05-19 16:07:41 -07:00
m_LastX = m_PrevX = m_X ;
m_LastZ = m_PrevZ = m_Z ;
2014-05-26 09:23:46 -07:00
m_LastYDifference = entity_pos_t : : Zero ( ) ;
2010-01-09 11:20:14 -08:00
}
2010-01-29 13:13:18 -08:00
2010-04-29 16:36:05 -07:00
AdvertisePositionChanges ( ) ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2010-01-09 11:20:14 -08:00
}
2016-11-23 06:09:58 -08:00
2022-03-07 15:04:11 -08:00
void MoveAndTurnTo ( entity_pos_t x , entity_pos_t z , entity_angle_t ry ) override
2013-12-04 09:38:46 -08:00
{
m_X = x ;
m_Z = z ;
2016-11-23 06:09:58 -08:00
2013-12-04 09:38:46 -08:00
if ( ! m_InWorld )
{
m_InWorld = true ;
m_LastX = m_PrevX = m_X ;
m_LastZ = m_PrevZ = m_Z ;
2014-05-26 09:23:46 -07:00
m_LastYDifference = entity_pos_t : : Zero ( ) ;
2013-12-04 09:38:46 -08:00
}
2016-11-23 06:09:58 -08:00
2014-05-30 07:46:06 -07:00
// TurnTo will advertise the position changes
TurnTo ( ry ) ;
2014-06-01 12:06:58 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2013-12-04 09:38:46 -08:00
}
2010-01-09 11:20:14 -08:00
2022-03-07 15:04:11 -08:00
void JumpTo ( entity_pos_t x , entity_pos_t z ) override
2010-01-09 11:20:14 -08:00
{
2012-05-19 16:07:41 -07:00
m_LastX = m_PrevX = m_X = x ;
m_LastZ = m_PrevZ = m_Z = z ;
2010-01-09 11:20:14 -08:00
m_InWorld = true ;
2010-01-29 13:13:18 -08:00
2013-07-15 20:46:30 -07:00
UpdateXZRotation ( ) ;
m_LastInterpolatedRotX = m_InterpolatedRotX ;
m_LastInterpolatedRotZ = m_InterpolatedRotZ ;
2010-04-29 16:36:05 -07:00
AdvertisePositionChanges ( ) ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
void SetHeightOffset ( entity_pos_t dy ) override
2010-01-09 11:20:14 -08:00
{
2014-05-26 09:23:46 -07:00
// subtract the offset and replace with a new offset
m_LastYDifference = dy - GetHeightOffset ( ) ;
m_Y + = m_LastYDifference ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
entity_pos_t GetHeightOffset ( ) const override
2010-01-09 11:20:14 -08:00
{
2014-05-26 09:23:46 -07:00
if ( m_RelativeToGround )
return m_Y ;
// not relative to the ground, so the height offset is m_Y - ground height
2017-09-17 20:55:33 -07:00
// except when floating, when the height offset is m_Y - water level + float depth
2014-05-26 09:23:46 -07:00
entity_pos_t baseY ;
CmpPtr < ICmpTerrain > cmpTerrain ( GetSystemEntity ( ) ) ;
if ( cmpTerrain )
baseY = cmpTerrain - > GetGroundLevel ( m_X , m_Z ) ;
if ( m_Floating )
{
CmpPtr < ICmpWaterManager > cmpWaterManager ( GetSystemEntity ( ) ) ;
if ( cmpWaterManager )
2017-09-17 20:55:33 -07:00
baseY = std : : max ( baseY , cmpWaterManager - > GetWaterLevel ( m_X , m_Z ) - m_FloatDepth ) ;
2014-05-26 09:23:46 -07:00
}
return m_Y - baseY ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
void SetHeightFixed ( entity_pos_t y ) override
2011-06-09 12:44:40 -07:00
{
2014-05-26 09:23:46 -07:00
// subtract the absolute height and replace it with a new absolute height
m_LastYDifference = y - GetHeightFixed ( ) ;
m_Y + = m_LastYDifference ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2014-05-26 09:23:46 -07:00
}
2022-03-07 15:04:11 -08:00
entity_pos_t GetHeightFixed ( ) const override
2021-01-22 10:16:13 -08:00
{
return GetHeightAtFixed ( m_X , m_Z ) ;
}
2022-03-07 15:04:11 -08:00
entity_pos_t GetHeightAtFixed ( entity_pos_t x , entity_pos_t z ) const override
2014-05-26 09:23:46 -07:00
{
if ( ! m_RelativeToGround )
return m_Y ;
// relative to the ground, so the fixed height = ground height + m_Y
2017-09-17 20:55:33 -07:00
// except when floating, when the fixed height = water level - float depth + m_Y
2014-05-26 09:23:46 -07:00
entity_pos_t baseY ;
CmpPtr < ICmpTerrain > cmpTerrain ( GetSystemEntity ( ) ) ;
if ( cmpTerrain )
2021-01-22 10:16:13 -08:00
baseY = cmpTerrain - > GetGroundLevel ( x , z ) ;
2014-05-26 09:23:46 -07:00
if ( m_Floating )
2013-08-15 01:44:06 -07:00
{
2014-05-26 09:23:46 -07:00
CmpPtr < ICmpWaterManager > cmpWaterManager ( GetSystemEntity ( ) ) ;
if ( cmpWaterManager )
2021-01-22 10:16:13 -08:00
baseY = std : : max ( baseY , cmpWaterManager - > GetWaterLevel ( x , z ) - m_FloatDepth ) ;
2013-08-15 01:44:06 -07:00
}
2014-05-26 09:23:46 -07:00
return m_Y + baseY ;
}
2022-03-07 15:04:11 -08:00
bool IsHeightRelative ( ) const override
2014-05-26 09:23:46 -07:00
{
return m_RelativeToGround ;
}
2022-03-07 15:04:11 -08:00
void SetHeightRelative ( bool relative ) override
2014-05-26 09:23:46 -07:00
{
// move y to use the right offset (from terrain or from map origin)
m_Y = relative ? GetHeightOffset ( ) : GetHeightFixed ( ) ;
m_RelativeToGround = relative ;
m_LastYDifference = entity_pos_t : : Zero ( ) ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
2011-06-09 12:44:40 -07:00
}
2022-03-07 15:04:11 -08:00
bool CanFloat ( ) const override
2010-05-27 16:23:53 -07:00
{
return m_Floating ;
}
2022-03-07 15:04:11 -08:00
void SetFloating ( bool flag ) override
2014-05-26 09:23:46 -07:00
{
m_Floating = flag ;
2014-06-01 11:24:50 -07:00
AdvertiseInterpolatedPositionChanges ( ) ;
}
2022-03-07 15:04:11 -08:00
void SetActorFloating ( bool flag ) override
2014-06-01 11:24:50 -07:00
{
m_ActorFloating = flag ;
AdvertiseInterpolatedPositionChanges ( ) ;
}
2025-01-23 12:47:26 -08:00
void SetActorAnchor ( const CStr & anchor ) override
{
m_ActorAnchorType = ParseAnchorString ( anchor ) ;
}
2022-03-07 15:04:11 -08:00
void SetConstructionProgress ( fixed progress ) override
2014-06-01 11:24:50 -07:00
{
m_ConstructionProgress = progress ;
AdvertiseInterpolatedPositionChanges ( ) ;
2014-05-26 09:23:46 -07:00
}
2022-03-07 15:04:11 -08:00
CFixedVector3D GetPosition ( ) const override
2010-01-09 11:20:14 -08:00
{
if ( ! m_InWorld )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::GetPosition called on entity when IsInWorld is false " ) ;
2010-01-09 11:20:14 -08:00
return CFixedVector3D ( ) ;
}
2014-05-26 09:23:46 -07:00
return CFixedVector3D ( m_X , GetHeightFixed ( ) , m_Z ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
CFixedVector2D GetPosition2D ( ) const override
2010-07-29 13:39:23 -07:00
{
if ( ! m_InWorld )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::GetPosition2D called on entity when IsInWorld is false " ) ;
2010-07-29 13:39:23 -07:00
return CFixedVector2D ( ) ;
}
return CFixedVector2D ( m_X , m_Z ) ;
}
2022-03-07 15:04:11 -08:00
CFixedVector3D GetPreviousPosition ( ) const override
2016-11-23 05:02:58 -08:00
{
if ( ! m_InWorld )
{
LOGERROR ( " CCmpPosition::GetPreviousPosition called on entity when IsInWorld is false " ) ;
return CFixedVector3D ( ) ;
}
2012-05-19 16:07:41 -07:00
2021-01-22 10:16:13 -08:00
return CFixedVector3D ( m_PrevX , GetHeightAtFixed ( m_PrevX , m_PrevZ ) , m_PrevZ ) ;
2016-11-23 05:02:58 -08:00
}
2012-05-19 16:07:41 -07:00
2022-03-07 15:04:11 -08:00
CFixedVector2D GetPreviousPosition2D ( ) const override
2016-11-23 05:02:58 -08:00
{
if ( ! m_InWorld )
{
LOGERROR ( " CCmpPosition::GetPreviousPosition2D called on entity when IsInWorld is false " ) ;
return CFixedVector2D ( ) ;
}
2012-05-19 16:07:41 -07:00
2016-11-23 05:02:58 -08:00
return CFixedVector2D ( m_PrevX , m_PrevZ ) ;
2012-05-19 16:07:41 -07:00
}
2022-03-07 15:04:11 -08:00
fixed GetTurnRate ( ) const override
2020-12-17 14:54:14 -08:00
{
return m_RotYSpeed ;
}
2022-03-07 15:04:11 -08:00
void TurnTo ( entity_angle_t y ) override
2010-02-10 11:28:46 -08:00
{
2014-05-30 07:46:06 -07:00
if ( m_TurretParent ! = INVALID_ENTITY )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( cmpPosition )
y - = cmpPosition - > GetRotation ( ) . Y ;
}
2023-06-14 00:23:05 -07:00
2010-02-10 11:28:46 -08:00
m_RotY = y ;
2010-04-29 16:36:05 -07:00
AdvertisePositionChanges ( ) ;
2014-06-19 16:20:12 -07:00
UpdateMessageSubscriptions ( ) ;
2010-02-10 11:28:46 -08:00
}
2022-03-07 15:04:11 -08:00
void SetYRotation ( entity_angle_t y ) override
2010-01-09 11:20:14 -08:00
{
2014-05-30 07:46:06 -07:00
if ( m_TurretParent ! = INVALID_ENTITY )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( cmpPosition )
y - = cmpPosition - > GetRotation ( ) . Y ;
}
2010-01-09 11:20:14 -08:00
m_RotY = y ;
2010-02-10 11:28:46 -08:00
m_InterpolatedRotY = m_RotY . ToFloat ( ) ;
2010-01-29 13:13:18 -08:00
2013-07-15 20:46:30 -07:00
if ( m_InWorld )
{
UpdateXZRotation ( ) ;
m_LastInterpolatedRotX = m_InterpolatedRotX ;
m_LastInterpolatedRotZ = m_InterpolatedRotZ ;
}
2010-04-29 16:36:05 -07:00
AdvertisePositionChanges ( ) ;
2014-06-19 16:20:12 -07:00
UpdateMessageSubscriptions ( ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
void SetXZRotation ( entity_angle_t x , entity_angle_t z ) override
2010-01-09 11:20:14 -08:00
{
m_RotX = x ;
m_RotZ = z ;
2010-01-29 13:13:18 -08:00
2013-07-15 20:46:30 -07:00
if ( m_InWorld )
{
UpdateXZRotation ( ) ;
m_LastInterpolatedRotX = m_InterpolatedRotX ;
m_LastInterpolatedRotZ = m_InterpolatedRotZ ;
}
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
CFixedVector3D GetRotation ( ) const override
2010-01-09 11:20:14 -08:00
{
2014-05-30 07:46:06 -07:00
entity_angle_t y = m_RotY ;
if ( m_TurretParent ! = INVALID_ENTITY )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( cmpPosition )
y + = cmpPosition - > GetRotation ( ) . Y ;
}
2014-07-23 06:02:42 -07:00
return CFixedVector3D ( m_RotX , y , m_RotZ ) ;
2010-01-09 11:20:14 -08:00
}
2022-03-07 15:04:11 -08:00
fixed GetDistanceTravelled ( ) const override
2010-09-03 02:55:14 -07:00
{
if ( ! m_InWorld )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::GetDistanceTravelled called on entity when IsInWorld is false " ) ;
2010-09-03 02:55:14 -07:00
return fixed : : Zero ( ) ;
}
return CFixedVector2D ( m_X - m_LastX , m_Z - m_LastZ ) . Length ( ) ;
}
2017-01-19 18:25:19 -08:00
float GetConstructionProgressOffset ( const CVector3D & pos ) const
2014-06-01 11:24:50 -07:00
{
if ( m_ConstructionProgress . IsZero ( ) )
return 0.0f ;
CmpPtr < ICmpVisual > cmpVisual ( GetEntityHandle ( ) ) ;
if ( ! cmpVisual )
return 0.0f ;
// We use selection boxes to calculate the model size, since the model could be offset
// TODO: this annoyingly shows decals, would be nice to hide them
CBoundingBoxOriented bounds = cmpVisual - > GetSelectionBox ( ) ;
if ( bounds . IsEmpty ( ) )
return 0.0f ;
float dy = 2.0f * bounds . m_HalfSizes . Y ;
// If this is a floating unit, we want it to start all the way under the terrain,
// so find the difference between its current position and the terrain
CmpPtr < ICmpTerrain > cmpTerrain ( GetSystemEntity ( ) ) ;
if ( cmpTerrain & & ( m_Floating | | m_ActorFloating ) )
{
float ground = cmpTerrain - > GetExactGroundLevel ( pos . X , pos . Z ) ;
dy + = std : : max ( 0.f , pos . Y - ground ) ;
}
return ( m_ConstructionProgress . ToFloat ( ) - 1.0f ) * dy ;
}
2022-03-07 15:04:11 -08:00
void GetInterpolatedPosition2D ( float frameOffset , float & x , float & z , float & rotY ) const override
2010-04-23 09:57:18 -07:00
{
if ( ! m_InWorld )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::GetInterpolatedPosition2D called on entity when IsInWorld is false " ) ;
2010-04-23 09:57:18 -07:00
return ;
}
x = Interpolate ( m_LastX . ToFloat ( ) , m_X . ToFloat ( ) , frameOffset ) ;
z = Interpolate ( m_LastZ . ToFloat ( ) , m_Z . ToFloat ( ) , frameOffset ) ;
rotY = m_InterpolatedRotY ;
}
2022-03-07 15:04:11 -08:00
CMatrix3D GetInterpolatedTransform ( float frameOffset ) const override
2010-01-09 11:20:14 -08:00
{
2014-05-30 07:46:06 -07:00
if ( m_TurretParent ! = INVALID_ENTITY )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , m_TurretParent ) ;
if ( ! cmpPosition )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " Turret with parent without position component " ) ;
2014-05-30 07:46:06 -07:00
CMatrix3D m ;
m . SetIdentity ( ) ;
return m ;
}
if ( ! cmpPosition - > IsInWorld ( ) )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::GetInterpolatedTransform called on turret entity when IsInWorld is false " ) ;
2014-05-30 07:46:06 -07:00
CMatrix3D m ;
m . SetIdentity ( ) ;
return m ;
}
else
{
2014-06-01 11:24:50 -07:00
CMatrix3D parentTransformMatrix = cmpPosition - > GetInterpolatedTransform ( frameOffset ) ;
2014-05-30 07:46:06 -07:00
CMatrix3D ownTransformation = CMatrix3D ( ) ;
ownTransformation . SetYRotation ( m_InterpolatedRotY ) ;
ownTransformation . Translate ( - m_TurretPosition . X . ToFloat ( ) , m_TurretPosition . Y . ToFloat ( ) , - m_TurretPosition . Z . ToFloat ( ) ) ;
return parentTransformMatrix * ownTransformation ;
}
}
2010-01-09 11:20:14 -08:00
if ( ! m_InWorld )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::GetInterpolatedTransform called on entity when IsInWorld is false " ) ;
2010-01-09 11:20:14 -08:00
CMatrix3D m ;
m . SetIdentity ( ) ;
return m ;
}
2024-12-02 08:07:55 -08:00
float x { 0.0f } , z { 0.0f } , rotY { 0.0f } ;
2010-04-23 09:57:18 -07:00
GetInterpolatedPosition2D ( frameOffset , x , z , rotY ) ;
2010-01-09 11:20:14 -08:00
2016-11-23 06:09:58 -08:00
2014-05-26 10:12:05 -07:00
float baseY = 0 ;
if ( m_RelativeToGround )
2010-01-09 11:20:14 -08:00
{
2013-09-11 13:41:53 -07:00
CmpPtr < ICmpTerrain > cmpTerrain ( GetSystemEntity ( ) ) ;
2014-05-26 10:12:05 -07:00
if ( cmpTerrain )
baseY = cmpTerrain - > GetExactGroundLevel ( x , z ) ;
2014-06-01 11:24:50 -07:00
if ( m_Floating | | m_ActorFloating )
2011-06-09 12:44:40 -07:00
{
2014-05-26 10:12:05 -07:00
CmpPtr < ICmpWaterManager > cmpWaterManager ( GetSystemEntity ( ) ) ;
if ( cmpWaterManager )
2017-09-17 20:55:33 -07:00
baseY = std : : max ( baseY , cmpWaterManager - > GetExactWaterLevel ( x , z ) - m_FloatDepth . ToFloat ( ) ) ;
2011-06-09 12:44:40 -07:00
}
2010-01-09 11:20:14 -08:00
}
2015-11-06 08:56:39 -08:00
float y = baseY + m_Y . ToFloat ( ) + Interpolate ( - 1 * m_LastYDifference . ToFloat ( ) , 0.f , frameOffset ) ;
2014-05-26 10:12:05 -07:00
2013-07-15 20:46:30 -07:00
CMatrix3D m ;
2014-07-14 10:15:22 -07:00
2016-11-23 05:02:58 -08:00
// linear interpolation is good enough (for RotX/Z).
2016-11-23 06:09:58 -08:00
// As you always stay close to zero angle.
2013-07-15 20:46:30 -07:00
m . SetXRotation ( Interpolate ( m_LastInterpolatedRotX , m_InterpolatedRotX , frameOffset ) ) ;
m . RotateZ ( Interpolate ( m_LastInterpolatedRotZ , m_InterpolatedRotZ , frameOffset ) ) ;
2014-07-14 10:15:22 -07:00
2014-06-01 11:24:50 -07:00
CVector3D pos ( x , y , z ) ;
pos . Y + = GetConstructionProgressOffset ( pos ) ;
2013-03-10 13:31:53 -07:00
m . RotateY ( rotY + ( float ) M_PI ) ;
2014-06-01 11:24:50 -07:00
m . Translate ( pos ) ;
2014-07-14 10:15:22 -07:00
2013-03-10 13:31:53 -07:00
return m ;
2010-01-09 11:20:14 -08:00
}
2017-01-19 18:25:19 -08:00
void GetInterpolatedPositions ( CVector3D & pos0 , CVector3D & pos1 ) const
2014-06-01 11:24:50 -07:00
{
float baseY0 = 0 ;
float baseY1 = 0 ;
float x0 = m_LastX . ToFloat ( ) ;
float z0 = m_LastZ . ToFloat ( ) ;
float x1 = m_X . ToFloat ( ) ;
float z1 = m_Z . ToFloat ( ) ;
if ( m_RelativeToGround )
{
CmpPtr < ICmpTerrain > cmpTerrain ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( cmpTerrain )
{
baseY0 = cmpTerrain - > GetExactGroundLevel ( x0 , z0 ) ;
baseY1 = cmpTerrain - > GetExactGroundLevel ( x1 , z1 ) ;
}
if ( m_Floating | | m_ActorFloating )
{
CmpPtr < ICmpWaterManager > cmpWaterManager ( GetSimContext ( ) , SYSTEM_ENTITY ) ;
if ( cmpWaterManager )
{
2017-09-17 20:55:33 -07:00
baseY0 = std : : max ( baseY0 , cmpWaterManager - > GetExactWaterLevel ( x0 , z0 ) - m_FloatDepth . ToFloat ( ) ) ;
baseY1 = std : : max ( baseY1 , cmpWaterManager - > GetExactWaterLevel ( x1 , z1 ) - m_FloatDepth . ToFloat ( ) ) ;
2014-06-01 11:24:50 -07:00
}
}
}
float y0 = baseY0 + m_Y . ToFloat ( ) + m_LastYDifference . ToFloat ( ) ;
float y1 = baseY1 + m_Y . ToFloat ( ) ;
pos0 = CVector3D ( x0 , y0 , z0 ) ;
pos1 = CVector3D ( x1 , y1 , z1 ) ;
pos0 . Y + = GetConstructionProgressOffset ( pos0 ) ;
pos1 . Y + = GetConstructionProgressOffset ( pos1 ) ;
}
2022-03-07 15:04:11 -08:00
void HandleMessage ( const CMessage & msg , bool UNUSED ( global ) ) override
2010-01-09 11:20:14 -08:00
{
switch ( msg . GetType ( ) )
{
2010-02-10 11:28:46 -08:00
case MT_Interpolate :
{
2016-06-25 06:12:35 -07:00
PROFILE ( " Position::Interpolate " ) ;
2014-06-19 16:20:12 -07:00
2010-02-10 11:28:46 -08:00
const CMessageInterpolate & msgData = static_cast < const CMessageInterpolate & > ( msg ) ;
float rotY = m_RotY . ToFloat ( ) ;
2013-07-15 20:46:30 -07:00
if ( rotY ! = m_InterpolatedRotY )
{
2020-12-17 14:54:14 -08:00
float rotYSpeed = m_RotYSpeed . ToFloat ( ) ;
2013-07-15 20:46:30 -07:00
float delta = rotY - m_InterpolatedRotY ;
// Wrap delta to -M_PI..M_PI
delta = fmodf ( delta + ( float ) M_PI , 2 * ( float ) M_PI ) ; // range -2PI..2PI
if ( delta < 0 ) delta + = 2 * ( float ) M_PI ; // range 0..2PI
delta - = ( float ) M_PI ; // range -M_PI..M_PI
// Clamp to max rate
2020-12-17 14:54:14 -08:00
float deltaClamped = Clamp ( delta , - rotYSpeed * msgData . deltaSimTime , + rotYSpeed * msgData . deltaSimTime ) ;
2013-07-15 20:46:30 -07:00
// Calculate new orientation, in a peculiar way in order to make sure the
// result gets close to m_orientation (rather than being n*2*M_PI out)
m_InterpolatedRotY = rotY + deltaClamped - delta ;
2016-11-23 06:09:58 -08:00
2013-07-15 20:46:30 -07:00
// update the visual XZ rotation
if ( m_InWorld )
{
m_LastInterpolatedRotX = m_InterpolatedRotX ;
m_LastInterpolatedRotZ = m_InterpolatedRotZ ;
UpdateXZRotation ( ) ;
}
2014-06-19 16:20:12 -07:00
UpdateMessageSubscriptions ( ) ;
2013-07-15 20:46:30 -07:00
}
2010-02-10 11:28:46 -08:00
break ;
}
2010-01-09 11:20:14 -08:00
case MT_TurnStart :
2010-02-10 11:28:46 -08:00
{
2016-11-23 06:09:58 -08:00
2013-07-15 20:46:30 -07:00
m_LastInterpolatedRotX = m_InterpolatedRotX ;
m_LastInterpolatedRotZ = m_InterpolatedRotZ ;
if ( m_InWorld & & ( m_LastX ! = m_X | | m_LastZ ! = m_Z ) )
UpdateXZRotation ( ) ;
2012-05-19 16:07:41 -07:00
// Store the positions from the turn before
m_PrevX = m_LastX ;
m_PrevZ = m_LastZ ;
2013-07-15 20:46:30 -07:00
2010-01-09 11:20:14 -08:00
m_LastX = m_X ;
m_LastZ = m_Z ;
2014-05-26 09:23:46 -07:00
m_LastYDifference = entity_pos_t : : Zero ( ) ;
2013-03-05 12:02:16 -08:00
2014-02-16 07:22:11 -08:00
break ;
}
2014-06-01 11:24:50 -07:00
case MT_TerrainChanged :
case MT_WaterChanged :
{
AdvertiseInterpolatedPositionChanges ( ) ;
break ;
}
case MT_Deserialized :
{
Deserialized ( ) ;
break ;
}
}
2010-01-09 11:20:14 -08:00
}
2010-04-29 16:36:05 -07:00
private :
2014-06-01 11:24:50 -07:00
2025-01-23 12:47:26 -08:00
AnchorType ParseAnchorString ( const CStr & anchor )
{
if ( anchor = = " pitch " )
return AnchorType : : PITCH ;
if ( anchor = = " roll " )
return AnchorType : : ROLL ;
if ( anchor = = " pitch-roll " )
return AnchorType : : PITCH_ROLL ;
return AnchorType : : UPRIGHT ;
}
2014-06-19 16:20:12 -07:00
/*
* Must be called whenever m_RotY or m_InterpolatedRotY change ,
* to determine whether we need to call Interpolate to make the unit rotate .
*/
void UpdateMessageSubscriptions ( )
{
bool needInterpolate = false ;
float rotY = m_RotY . ToFloat ( ) ;
if ( rotY ! = m_InterpolatedRotY )
needInterpolate = true ;
if ( needInterpolate ! = m_EnabledMessageInterpolate )
{
GetSimContext ( ) . GetComponentManager ( ) . DynamicSubscriptionNonsync ( MT_Interpolate , this , needInterpolate ) ;
m_EnabledMessageInterpolate = needInterpolate ;
}
}
2014-06-01 11:24:50 -07:00
/**
* This must be called after changing anything that will affect the
* return value of GetPosition2D ( ) or GetRotation ( ) . Y :
* - m_InWorld
* - m_X , m_Z
* - m_RotY
*/
2017-01-19 18:25:19 -08:00
void AdvertisePositionChanges ( ) const
2010-04-29 16:36:05 -07:00
{
2014-05-30 07:46:06 -07:00
for ( std : : set < entity_id_t > : : const_iterator it = m_Turrets . begin ( ) ; it ! = m_Turrets . end ( ) ; + + it )
{
CmpPtr < ICmpPosition > cmpPosition ( GetSimContext ( ) , * it ) ;
if ( cmpPosition )
cmpPosition - > UpdateTurretPosition ( ) ;
}
2010-04-29 16:36:05 -07:00
if ( m_InWorld )
{
2010-07-29 13:39:23 -07:00
CMessagePositionChanged msg ( GetEntityId ( ) , true , m_X , m_Z , m_RotY ) ;
2010-05-01 02:48:39 -07:00
GetSimContext ( ) . GetComponentManager ( ) . PostMessage ( GetEntityId ( ) , msg ) ;
2010-04-29 16:36:05 -07:00
}
else
{
2010-07-29 13:39:23 -07:00
CMessagePositionChanged msg ( GetEntityId ( ) , false , entity_pos_t : : Zero ( ) , entity_pos_t : : Zero ( ) , entity_angle_t : : Zero ( ) ) ;
2010-05-01 02:48:39 -07:00
GetSimContext ( ) . GetComponentManager ( ) . PostMessage ( GetEntityId ( ) , msg ) ;
2010-04-29 16:36:05 -07:00
}
}
2013-07-15 20:46:30 -07:00
2014-06-01 11:24:50 -07:00
/**
* This must be called after changing anything that will affect the
* return value of GetInterpolatedPositions ( ) :
* - m_InWorld
* - m_X , m_Z
* - m_LastX , m_LastZ
* - m_Y , m_LastYDifference , m_RelativeToGround
* - If m_RelativeToGround , then the ground under this unit
* - If m_RelativeToGround & & m_Float , then the water level
*/
2017-01-19 18:25:19 -08:00
void AdvertiseInterpolatedPositionChanges ( ) const
2014-06-01 11:24:50 -07:00
{
if ( m_InWorld )
{
CVector3D pos0 , pos1 ;
GetInterpolatedPositions ( pos0 , pos1 ) ;
CMessageInterpolatedPositionChanged msg ( GetEntityId ( ) , true , pos0 , pos1 ) ;
GetSimContext ( ) . GetComponentManager ( ) . PostMessage ( GetEntityId ( ) , msg ) ;
}
else
{
CMessageInterpolatedPositionChanged msg ( GetEntityId ( ) , false , CVector3D ( ) , CVector3D ( ) ) ;
GetSimContext ( ) . GetComponentManager ( ) . PostMessage ( GetEntityId ( ) , msg ) ;
}
}
2013-07-15 20:46:30 -07:00
void UpdateXZRotation ( )
{
if ( ! m_InWorld )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " CCmpPosition::UpdateXZRotation called on entity when IsInWorld is false " ) ;
2013-07-15 20:46:30 -07:00
return ;
}
2025-01-23 12:47:26 -08:00
AnchorType anchor = m_ActorAnchorType = = AnchorType : : UNDEFINED ? m_AnchorType : m_ActorAnchorType ;
if ( anchor = = AnchorType : : UPRIGHT | | ! m_RotZ . IsZero ( ) | | ! m_RotX . IsZero ( ) )
2013-07-15 20:46:30 -07:00
{
// set the visual rotations to the ones fixed by the interface
m_InterpolatedRotX = m_RotX . ToFloat ( ) ;
m_InterpolatedRotZ = m_RotZ . ToFloat ( ) ;
return ;
}
2013-09-11 13:41:53 -07:00
CmpPtr < ICmpTerrain > cmpTerrain ( GetSystemEntity ( ) ) ;
2013-07-15 20:46:30 -07:00
if ( ! cmpTerrain | | ! cmpTerrain - > IsLoaded ( ) )
{
2015-01-22 12:31:30 -08:00
LOGERROR ( " Terrain not loaded " ) ;
2013-07-15 20:46:30 -07:00
return ;
}
2013-07-16 22:42:16 -07:00
// TODO: average normal (average all the tiles?) for big units or for buildings?
CVector3D normal = cmpTerrain - > CalcExactNormal ( m_X . ToFloat ( ) , m_Z . ToFloat ( ) ) ;
2013-07-15 20:46:30 -07:00
// rotate the normal so the positive x direction is in the direction of the unit
CVector2D projected = CVector2D ( normal . X , normal . Z ) ;
projected . Rotate ( m_InterpolatedRotY ) ;
normal . X = projected . X ;
normal . Z = projected . Y ;
// project and calculate the angles
2025-01-23 12:47:26 -08:00
if ( anchor = = AnchorType : : PITCH | | anchor = = AnchorType : : PITCH_ROLL )
2013-07-15 20:46:30 -07:00
m_InterpolatedRotX = - atan2 ( normal . Z , normal . Y ) ;
2025-01-23 12:47:26 -08:00
if ( anchor = = AnchorType : : ROLL | | anchor = = AnchorType : : PITCH_ROLL )
2013-07-15 20:46:30 -07:00
m_InterpolatedRotZ = atan2 ( normal . X , normal . Y ) ;
}
2010-01-09 11:20:14 -08:00
} ;
REGISTER_COMPONENT_TYPE ( Position )