1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #ifndef SOLID_OBJECT_H
4 #define SOLID_OBJECT_H
5 
6 #include "WorldObject.h"
7 #include "System/bitops.h"
8 #include "System/Matrix44f.h"
9 #include "System/type2.h"
10 #include "System/Misc/BitwiseEnum.h"
11 #include "System/Sync/SyncedFloat3.h"
12 #include "System/Sync/SyncedPrimitive.h"
13 
14 struct MoveDef;
15 struct CollisionVolume;
16 struct LocalModelPiece;
17 struct SolidObjectDef;
18 struct SolidObjectGroundDecal;
19 
20 struct DamageArray;
21 class CUnit;
22 
23 enum TerrainChangeTypes {
24 	TERRAINCHANGE_DAMAGE_RECALCULATION = 0, // update after regular explosion or terraform event
25 	TERRAINCHANGE_SQUARE_TYPEMAP_INDEX = 1, // update after typemap-index of a square changed (Lua)
26 	TERRAINCHANGE_TYPEMAP_SPEED_VALUES = 2, // update after speed-values of a terrain-type changed (Lua)
27 	TERRAINCHANGE_OBJECT_INSERTED      = 3,
28 	TERRAINCHANGE_OBJECT_INSERTED_YM   = 4,
29 	TERRAINCHANGE_OBJECT_DELETED       = 5,
30 };
31 
32 enum YardmapStates {
33 	YARDMAP_OPEN        = 0,    // always free      (    walkable      buildable)
34 //  YARDMAP_WALKABLE    = 4,    // open for walk    (    walkable, not buildable)
35 	YARDMAP_YARD        = 1,    // walkable when yard is open
36 	YARDMAP_YARDINV     = 2,    // walkable when yard is closed
37 	YARDMAP_BLOCKED     = 0xFF & ~YARDMAP_YARDINV, // always block     (not walkable, not buildable)
38 
39 	// helpers
40 	YARDMAP_YARDBLOCKED = YARDMAP_YARD,
41 	YARDMAP_YARDFREE    = ~YARDMAP_YARD,
42 	YARDMAP_GEO         = YARDMAP_BLOCKED,
43 };
44 typedef Bitwise::BitwiseEnum<YardmapStates> YardMapStatus;
45 
46 
47 
48 class CSolidObject: public CWorldObject {
49 public:
50 	CR_DECLARE(CSolidObject)
51 
52 	enum PhysicalState {
53 		// NOTE:
54 		//   {ONGROUND,*WATER} and INAIR are mutually exclusive
55 		//   {UNDERGROUND,UNDERWATER} are not (and are the only
56 		//   bits to take radius into account)
57 		// TODO:
58 		//   should isDead be on this list for spatial queries?
59 		PSTATE_BIT_ONGROUND    = (1 << 0),
60 		PSTATE_BIT_INWATER     = (1 << 1),
61 		PSTATE_BIT_UNDERWATER  = (1 << 2),
62 		PSTATE_BIT_UNDERGROUND = (1 << 3),
63 		PSTATE_BIT_INAIR       = (1 << 4),
64 		PSTATE_BIT_INVOID      = (1 << 5),
65 
66 		// special bits for impulse-affected objects that do
67 		// not get set automatically by UpdatePhysicalState;
68 		// also used by aircraft to control block / unblock
69 		// behavior
70 		// NOTE: FLYING DOES NOT ALWAYS IMPLY INAIR!
71 		PSTATE_BIT_MOVING   = (1 <<  6),
72 		PSTATE_BIT_FLYING   = (1 <<  7),
73 		PSTATE_BIT_FALLING  = (1 <<  8),
74 		PSTATE_BIT_SKIDDING = (1 <<  9),
75 		PSTATE_BIT_CRASHING = (1 << 10),
76 		PSTATE_BIT_BLOCKING = (1 << 11),
77 	};
78 	enum CollidableState {
79 		CSTATE_BIT_SOLIDOBJECTS = (1 << 0), // can be set while (physicalState & PSTATE_BIT_BLOCKING) == 0!
80 		CSTATE_BIT_PROJECTILES  = (1 << 1),
81 		CSTATE_BIT_QUADMAPRAYS  = (1 << 2),
82 	};
83 	enum DamageType {
84 		DAMAGE_EXPLOSION_WEAPON  = 0, // weapon-projectile that triggered GameHelper::Explosion (weaponDefID >= 0)
85 		DAMAGE_EXPLOSION_DEBRIS  = 1, // piece-projectile that triggered GameHelper::Explosion (weaponDefID < 0)
86 		DAMAGE_COLLISION_GROUND  = 2, // ground collision
87 		DAMAGE_COLLISION_OBJECT  = 3, // object collision
88 		DAMAGE_EXTSOURCE_FIRE    = 4,
89 		DAMAGE_EXTSOURCE_WATER   = 5, // lava/acid/etc
90 		DAMAGE_EXTSOURCE_KILLED  = 6,
91 		DAMAGE_EXTSOURCE_CRUSHED = 7,
92 	};
93 
94 	CSolidObject();
95 	virtual ~CSolidObject();
96 
AddBuildPower(CUnit * builder,float amount)97 	virtual bool AddBuildPower(CUnit* builder, float amount) { return false; }
DoDamage(const DamageArray & damages,const float3 & impulse,CUnit * attacker,int weaponDefID,int projectileID)98 	virtual void DoDamage(const DamageArray& damages, const float3& impulse, CUnit* attacker, int weaponDefID, int projectileID) {}
99 
ApplyImpulse(const float3 & impulse)100 	virtual void ApplyImpulse(const float3& impulse) { SetVelocity(speed + impulse); }
101 
102 	virtual void Kill(CUnit* killer, const float3& impulse, bool crushed);
GetBlockingMapID()103 	virtual int GetBlockingMapID() const { return -1; }
104 
ForcedMove(const float3 & newPos)105 	virtual void ForcedMove(const float3& newPos) {}
106 	virtual void ForcedSpin(const float3& newDir);
107 
108 	virtual void UpdatePhysicalState(float eps);
109 
Move(const float3 & v,bool relative)110 	void Move(const float3& v, bool relative) {
111 		const float3& dv = relative? v: (v - pos);
112 
113 		pos += dv;
114 		midPos += dv;
115 		aimPos += dv;
116 	}
117 
118 	// this should be called whenever the direction
119 	// vectors are changed (ie. after a rotation) in
120 	// eg. movetype code
UpdateMidAndAimPos()121 	void UpdateMidAndAimPos() {
122 		midPos = GetMidPos();
123 		aimPos = GetAimPos();
124 	}
SetMidAndAimPos(const float3 & mp,const float3 & ap,bool relative)125 	void SetMidAndAimPos(const float3& mp, const float3& ap, bool relative) {
126 		SetMidPos(mp, relative);
127 		SetAimPos(ap, relative);
128 	}
129 
130 
SetDirVectors(const CMatrix44f & matrix)131 	void SetDirVectors(const CMatrix44f& matrix) {
132 		rightdir.x = -matrix[0]; updir.x = matrix[4]; frontdir.x = matrix[ 8];
133 		rightdir.y = -matrix[1]; updir.y = matrix[5]; frontdir.y = matrix[ 9];
134 		rightdir.z = -matrix[2]; updir.z = matrix[6]; frontdir.z = matrix[10];
135 	}
136 	// update object's <heading> from current frontdir
137 	// should always be called after a SetDirVectors()
138 	void SetHeadingFromDirection();
139 	// update object's local coor-sys from current <heading>
140 	// (unlike ForcedSpin which updates from given <updir>)
141 	// NOTE: movetypes call this directly
142 	void UpdateDirVectors(bool useGroundNormal);
143 
144 	virtual CMatrix44f GetTransformMatrix(const bool synced = false, const bool error = false) const {
145 		// should never get called (should be pure virtual, but cause of CREG we cannot use it)
146 		assert(false);
147 		return CMatrix44f();
148 	}
149 
GetCollisionVolume(const LocalModelPiece * lmp)150 	virtual const CollisionVolume* GetCollisionVolume(const LocalModelPiece* lmp) const { return collisionVolume; }
151 
152 
153 	/**
154 	 * adds this object to the GroundBlockingMap if and only
155 	 * if HasCollidableStateBit(CSTATE_BIT_SOLIDOBJECTS), else
156 	 * does nothing
157 	 */
158 	void Block();
159 	/**
160 	 * Removes this object from the GroundBlockingMap if it
161 	 * is currently marked on it, does nothing otherwise.
162 	 */
163 	void UnBlock();
164 
165 	// these transform a point or vector to object-space
GetObjectSpaceVec(const float3 & v)166 	float3 GetObjectSpaceVec(const float3& v) const { return (      (frontdir * v.z) + (rightdir * v.x) + (updir * v.y)); }
GetObjectSpacePos(const float3 & p)167 	float3 GetObjectSpacePos(const float3& p) const { return (pos + (frontdir * p.z) + (rightdir * p.x) + (updir * p.y)); }
GetObjectSpacePosUnsynced(const float3 & p)168 	float3 GetObjectSpacePosUnsynced(const float3& p) const { return (drawPos + GetObjectSpaceVec(p)); }
169 
GetMapPos()170 	int2 GetMapPos() const { return (GetMapPos(pos)); }
171 	int2 GetMapPos(const float3& position) const;
172 
173 	float3 GetDragAccelerationVec(const float4& params) const;
174 	float3 GetWantedUpDir(bool useGroundNormal) const;
175 
176 	YardMapStatus GetGroundBlockingMaskAtPos(float3 gpos) const;
177 
BlockMapPosChanged()178 	bool BlockMapPosChanged() const { return (groundBlockPos != pos); }
179 
IsOnGround()180 	bool IsOnGround   () const { return (HasPhysicalStateBit(PSTATE_BIT_ONGROUND   )); }
IsInAir()181 	bool IsInAir      () const { return (HasPhysicalStateBit(PSTATE_BIT_INAIR      )); }
IsInWater()182 	bool IsInWater    () const { return (HasPhysicalStateBit(PSTATE_BIT_INWATER    )); }
IsUnderWater()183 	bool IsUnderWater () const { return (HasPhysicalStateBit(PSTATE_BIT_UNDERWATER )); }
IsUnderGround()184 	bool IsUnderGround() const { return (HasPhysicalStateBit(PSTATE_BIT_UNDERGROUND)); }
IsInVoid()185 	bool IsInVoid     () const { return (HasPhysicalStateBit(PSTATE_BIT_INVOID     )); }
186 
IsMoving()187 	bool IsMoving  () const { return (HasPhysicalStateBit(PSTATE_BIT_MOVING  )); }
IsFlying()188 	bool IsFlying  () const { return (HasPhysicalStateBit(PSTATE_BIT_FLYING  )); }
IsFalling()189 	bool IsFalling () const { return (HasPhysicalStateBit(PSTATE_BIT_FALLING )); }
IsSkidding()190 	bool IsSkidding() const { return (HasPhysicalStateBit(PSTATE_BIT_SKIDDING)); }
IsCrashing()191 	bool IsCrashing() const { return (HasPhysicalStateBit(PSTATE_BIT_CRASHING)); }
IsBlocking()192 	bool IsBlocking() const { return (HasPhysicalStateBit(PSTATE_BIT_BLOCKING)); }
193 
HasPhysicalStateBit(unsigned int bit)194 	bool    HasPhysicalStateBit(unsigned int bit) const { return ((physicalState & bit) != 0); }
SetPhysicalStateBit(unsigned int bit)195 	void    SetPhysicalStateBit(unsigned int bit) { unsigned int ps = physicalState; ps |= ( bit); physicalState = static_cast<PhysicalState>(ps); }
ClearPhysicalStateBit(unsigned int bit)196 	void  ClearPhysicalStateBit(unsigned int bit) { unsigned int ps = physicalState; ps &= (~bit); physicalState = static_cast<PhysicalState>(ps); }
PushPhysicalStateBit(unsigned int bit)197 	void   PushPhysicalStateBit(unsigned int bit) { UpdatePhysicalStateBit(1u << (32u - (bits_ffs(bit) - 1u)), HasPhysicalStateBit(bit)); }
PopPhysicalStateBit(unsigned int bit)198 	void    PopPhysicalStateBit(unsigned int bit) { UpdatePhysicalStateBit(bit, HasPhysicalStateBit(1u << (32u - (bits_ffs(bit) - 1u)))); }
UpdatePhysicalStateBit(unsigned int bit,bool set)199 	bool UpdatePhysicalStateBit(unsigned int bit, bool set) {
200 		if (set) {
201 			SetPhysicalStateBit(bit);
202 		} else {
203 			ClearPhysicalStateBit(bit);
204 		}
205 		return (HasPhysicalStateBit(bit));
206 	}
207 
HasCollidableStateBit(unsigned int bit)208 	bool    HasCollidableStateBit(unsigned int bit) const { return ((collidableState & bit) != 0); }
SetCollidableStateBit(unsigned int bit)209 	void    SetCollidableStateBit(unsigned int bit) { unsigned int cs = collidableState; cs |= ( bit); collidableState = static_cast<CollidableState>(cs); }
ClearCollidableStateBit(unsigned int bit)210 	void  ClearCollidableStateBit(unsigned int bit) { unsigned int cs = collidableState; cs &= (~bit); collidableState = static_cast<CollidableState>(cs); }
PushCollidableStateBit(unsigned int bit)211 	void   PushCollidableStateBit(unsigned int bit) { UpdateCollidableStateBit(1u << (32u - (bits_ffs(bit) - 1u)), HasCollidableStateBit(bit)); }
PopCollidableStateBit(unsigned int bit)212 	void    PopCollidableStateBit(unsigned int bit) { UpdateCollidableStateBit(bit, HasCollidableStateBit(1u << (32u - (bits_ffs(bit) - 1u)))); }
UpdateCollidableStateBit(unsigned int bit,bool set)213 	bool UpdateCollidableStateBit(unsigned int bit, bool set) {
214 		if (set) {
215 			SetCollidableStateBit(bit);
216 		} else {
217 			ClearCollidableStateBit(bit);
218 		}
219 		return (HasCollidableStateBit(bit));
220 	}
221 
222 	bool SetVoidState();
223 	bool ClearVoidState();
224 	void UpdateVoidState(bool set);
225 
226 private:
SetMidPos(const float3 & mp,bool relative)227 	void SetMidPos(const float3& mp, bool relative) {
228 		if (relative) {
229 			relMidPos = mp; midPos = GetMidPos();
230 		} else {
231 			midPos = mp; relMidPos = midPos - pos;
232 		}
233 	}
SetAimPos(const float3 & ap,bool relative)234 	void SetAimPos(const float3& ap, bool relative) {
235 		if (relative) {
236 			relAimPos = ap; aimPos = GetAimPos();
237 		} else {
238 			aimPos = ap; relAimPos = aimPos - pos;
239 		}
240 	}
241 
GetMidPos()242 	float3 GetMidPos() const { return (GetObjectSpacePos(relMidPos)); }
GetAimPos()243 	float3 GetAimPos() const { return (GetObjectSpacePos(relAimPos)); }
244 
245 public:
246 	float health;
247 	float mass;                                 ///< the physical mass of this object (run-time constant)
248 	float crushResistance;                      ///< how much MoveDef::crushStrength is required to crush this object (run-time constant)
249 
250 	bool crushable;                             ///< whether this object can potentially be crushed during a collision with another object
251 	bool immobile;                              ///< whether this object can be moved or not (except perhaps along y-axis, to make it stay on ground)
252 	bool blockEnemyPushing;                     ///< if false, object can be pushed during enemy collisions even when modrules forbid it
253 	bool blockHeightChanges;                    ///< if true, map height cannot change under this object (through explosions, etc.)
254 
255 	bool luaDraw;                               ///< if true, LuaRules::Draw{Unit, Feature} will be called for this object (UNSYNCED)
256 	bool noSelect;                              ///< if true, unit/feature can not be selected/mouse-picked by a player (UNSYNCED)
257 
258 	int xsize;                                  ///< The x-size of this object, according to its footprint. (Note: this is rotated depending on buildFacing!!!)
259 	int zsize;                                  ///< The z-size of this object, according to its footprint. (Note: this is rotated depending on buildFacing!!!)
260 	int2 footprint;                             ///< The unrotated x-/z-size of this object, according to its footprint.
261 
262 	SyncedSshort heading;                       ///< Contains the same information as frontdir, but in a short signed integer.
263 	PhysicalState physicalState;                ///< bitmask indicating current state of this object within the game world
264 	CollidableState collidableState;            ///< bitmask indicating which types of objects this object can collide with
265 
266 	int team;                                   ///< team that "owns" this object
267 	int allyteam;                               ///< allyteam that this->team is part of
268 
269 	int tempNum;                                ///< used to check if object has already been processed (in QuadField queries, etc)
270 
271 	const SolidObjectDef* objectDef;            ///< points to a UnitDef or to a FeatureDef instance
272 
273 	MoveDef* moveDef;                           ///< mobility information about this object (if NULL, object is either static or aircraft)
274 	CollisionVolume* collisionVolume;
275 	SolidObjectGroundDecal* groundDecal;
276 
277 	SyncedFloat3 frontdir;                      ///< object-local z-axis (in WS)
278 	SyncedFloat3 rightdir;                      ///< object-local x-axis (in WS)
279 	SyncedFloat3 updir;                         ///< object-local y-axis (in WS)
280 
281 	SyncedFloat3 relMidPos;                     ///< local-space vector from pos to midPos (read from model, used to initialize midPos)
282 	SyncedFloat3 relAimPos;                     ///< local-space vector from pos to aimPos (read from model, used to initialize aimPos)
283 	SyncedFloat3 midPos;                        ///< mid-position of model in WS, used as center of mass (and many other things)
284 	SyncedFloat3 aimPos;                        ///< used as aiming position by weapons
285 
286 	int2 mapPos;                                ///< current position on GroundBlockingObjectMap
287 	float3 groundBlockPos;
288 
289 	float3 dragScales;
290 
291 	float3 drawPos;                             ///< = pos + speed * timeOffset (unsynced)
292 	float3 drawMidPos;                          ///< = drawPos + relMidPos (unsynced)
293 
294 	const YardMapStatus* blockMap;              ///< Current (unrotated!) blockmap/yardmap of this object. 0 means no active yardmap => all blocked.
295 	short int buildFacing;                      ///< Orientation of footprint, 4 different states
296 
297 	static const float DEFAULT_MASS;
298 	static const float MINIMUM_MASS;
299 	static const float MAXIMUM_MASS;
300 
301 	static int deletingRefID;
SetDeletingRefID(int id)302 	static void SetDeletingRefID(int id) { deletingRefID = id; }
303 	// returns the object (command reference) id of the object currently being deleted,
304 	// for units this equals unit->id, and for features feature->id + unitHandler->MaxUnits()
GetDeletingRefID()305 	static int GetDeletingRefID() { return deletingRefID; }
306 };
307 
308 #endif // SOLID_OBJECT_H
309