1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <assert.h>
4 
5 #include "GroundBlockingObjectMap.h"
6 
7 #include "GlobalSynced.h"
8 #include "GlobalConstants.h"
9 #include "Sim/Objects/SolidObject.h"
10 #include "Sim/Objects/SolidObjectDef.h"
11 #include "Sim/Path/IPathManager.h"
12 #include "System/creg/STL_Map.h"
13 
14 CGroundBlockingObjectMap* groundBlockingObjectMap;
15 
16 CR_BIND(CGroundBlockingObjectMap, (1))
17 CR_REG_METADATA(CGroundBlockingObjectMap, (
18 	CR_MEMBER(groundBlockingMap)
19 ))
20 
21 
22 
GetObjectID(CSolidObject * obj)23 inline static const int GetObjectID(CSolidObject* obj)
24 {
25 	const int id = obj->GetBlockingMapID();
26 	// object should always be a derived type
27 	assert(id != -1);
28 	return id;
29 }
30 
31 
AddGroundBlockingObject(CSolidObject * object)32 void CGroundBlockingObjectMap::AddGroundBlockingObject(CSolidObject* object)
33 {
34 	if (object->blockMap != NULL) {
35 		// if object has a yardmap, add it to map selectively
36 		// (checking the specific state of each yardmap cell)
37 		AddGroundBlockingObject(object, YARDMAP_BLOCKED);
38 		return;
39 	}
40 
41 	const int objID = GetObjectID(object);
42 
43 	object->SetPhysicalStateBit(CSolidObject::PSTATE_BIT_BLOCKING);
44 	object->mapPos = object->GetMapPos();
45 	object->groundBlockPos = object->pos;
46 
47 	const int bx = object->mapPos.x, sx = object->xsize;
48 	const int bz = object->mapPos.y, sz = object->zsize;
49 	const int minXSqr = bx, maxXSqr = bx + sx;
50 	const int minZSqr = bz, maxZSqr = bz + sz;
51 
52 	for (int zSqr = minZSqr; zSqr < maxZSqr; zSqr++) {
53 		for (int xSqr = minXSqr; xSqr < maxXSqr; xSqr++) {
54 			BlockingMapCell& cell = groundBlockingMap[xSqr + zSqr * gs->mapx];
55 			cell[objID] = object;
56 		}
57 	}
58 
59 	// FIXME: needs dependency injection (observer pattern?)
60 	if (object->moveDef == NULL && pathManager != NULL) {
61 		pathManager->TerrainChange(minXSqr, minZSqr, maxXSqr, maxZSqr, TERRAINCHANGE_OBJECT_INSERTED);
62 	}
63 }
64 
AddGroundBlockingObject(CSolidObject * object,const YardMapStatus & mask)65 void CGroundBlockingObjectMap::AddGroundBlockingObject(CSolidObject* object, const YardMapStatus& mask)
66 {
67 	const int objID = GetObjectID(object);
68 
69 	object->SetPhysicalStateBit(CSolidObject::PSTATE_BIT_BLOCKING);
70 	object->mapPos = object->GetMapPos();
71 	object->groundBlockPos = object->pos;
72 
73 	const int bx = object->mapPos.x, sx = object->xsize;
74 	const int bz = object->mapPos.y, sz = object->zsize;
75 	const int minXSqr = bx, maxXSqr = bx + sx;
76 	const int minZSqr = bz, maxZSqr = bz + sz;
77 
78 	for (int z = minZSqr; z < maxZSqr; z++) {
79 		for (int x = minXSqr; x < maxXSqr; x++) {
80 			// unit yardmaps always contain sx=UnitDef::xsize * sz=UnitDef::zsize
81 			// cells (the unit->moveDef footprint can have different dimensions)
82 			const float3 testPos = float3(x, 0.0f, z) * SQUARE_SIZE;
83 
84 			if (object->GetGroundBlockingMaskAtPos(testPos) & mask) {
85 				BlockingMapCell& cell = groundBlockingMap[x + (z) * gs->mapx];
86 				cell[objID] = object;
87 			}
88 		}
89 	}
90 
91 	// FIXME: needs dependency injection (observer pattern?)
92 	if (object->moveDef == NULL && pathManager != NULL) {
93 		pathManager->TerrainChange(minXSqr, minZSqr, maxXSqr, maxZSqr, TERRAINCHANGE_OBJECT_INSERTED_YM);
94 	}
95 }
96 
97 
RemoveGroundBlockingObject(CSolidObject * object)98 void CGroundBlockingObjectMap::RemoveGroundBlockingObject(CSolidObject* object)
99 {
100 	const int objID = GetObjectID(object);
101 
102 	const int bx = object->mapPos.x;
103 	const int bz = object->mapPos.y;
104 	const int sx = object->xsize;
105 	const int sz = object->zsize;
106 
107 	object->ClearPhysicalStateBit(CSolidObject::PSTATE_BIT_BLOCKING);
108 
109 	for (int z = bz; z < bz + sz; ++z) {
110 		for (int x = bx; x < bx + sx; ++x) {
111 			const int idx = x + z * gs->mapx;
112 
113 			BlockingMapCell& cell = groundBlockingMap[idx];
114 			cell.erase(objID);
115 		}
116 	}
117 
118 	// FIXME: needs dependency injection (observer pattern?)
119 	if (object->moveDef == NULL && pathManager != NULL) {
120 		pathManager->TerrainChange(bx, bz, bx + sx, bz + sz, TERRAINCHANGE_OBJECT_DELETED);
121 	}
122 }
123 
124 
125 /**
126   * Checks if a ground-square is blocked.
127   * If it's not blocked (empty), then NULL is returned. Otherwise, a
128   * pointer to the top-most / bottom-most blocking object is returned.
129   */
GroundBlockedUnsafe(int mapSquare) const130 CSolidObject* CGroundBlockingObjectMap::GroundBlockedUnsafe(int mapSquare) const {
131 	const BlockingMapCell& cell = groundBlockingMap[mapSquare];
132 
133 	if (cell.empty())
134 		return NULL;
135 
136 	return ((cell.begin())->second);
137 }
138 
139 
GroundBlocked(int x,int z) const140 CSolidObject* CGroundBlockingObjectMap::GroundBlocked(int x, int z) const {
141 	if (x < 0 || x >= gs->mapx || z < 0 || z >= gs->mapy)
142 		return NULL;
143 
144 	return GroundBlockedUnsafe(x + z * gs->mapx);
145 }
146 
147 
GroundBlocked(const float3 & pos) const148 CSolidObject* CGroundBlockingObjectMap::GroundBlocked(const float3& pos) const {
149 	const int xSqr = int(pos.x / SQUARE_SIZE);
150 	const int zSqr = int(pos.z / SQUARE_SIZE);
151 	return GroundBlocked(xSqr, zSqr);
152 }
153 
154 
GroundBlocked(int x,int z,CSolidObject * ignoreObj) const155 bool CGroundBlockingObjectMap::GroundBlocked(int x, int z, CSolidObject* ignoreObj) const
156 {
157 	if (x < 0 || x >= gs->mapx || z < 0 || z >= gs->mapy)
158 		return false;
159 
160 	const int mapSquare = x + z * gs->mapx;
161 
162 	if (groundBlockingMap[mapSquare].empty())
163 		return false;
164 
165 	const int objID = GetObjectID(ignoreObj);
166 	const BlockingMapCell& cell = groundBlockingMap[mapSquare];
167 
168 	BlockingMapCellIt it = cell.begin();
169 
170 	if (it != cell.end()) {
171 		if (it->first != objID) {
172 			// there are other objects blocking the square
173 			return true;
174 		} else {
175 			// ignoreObj is in the square. Check if there are other objects, too
176 			return (cell.size() >= 2);
177 		}
178 	}
179 
180 	return false;
181 }
182 
183 
GroundBlocked(const float3 & pos,CSolidObject * ignoreObj) const184 bool CGroundBlockingObjectMap::GroundBlocked(const float3& pos, CSolidObject* ignoreObj) const
185 {
186 	const int xSqr = int(pos.x / SQUARE_SIZE);
187 	const int zSqr = int(pos.z / SQUARE_SIZE);
188 	return GroundBlocked(xSqr, zSqr, ignoreObj);
189 }
190 
191 
192 
193 /**
194   * Opens up a yard in a blocked area.
195   * When a factory opens up, for example.
196   */
OpenBlockingYard(CSolidObject * object)197 void CGroundBlockingObjectMap::OpenBlockingYard(CSolidObject* object) {
198 	RemoveGroundBlockingObject(object);
199 	AddGroundBlockingObject(object, YARDMAP_YARDFREE);
200 }
201 
202 /**
203   * Closes a yard, blocking the area.
204   * When a factory closes, for example.
205   */
CloseBlockingYard(CSolidObject * object)206 void CGroundBlockingObjectMap::CloseBlockingYard(CSolidObject* object) {
207 	RemoveGroundBlockingObject(object);
208 	AddGroundBlockingObject(object, YARDMAP_YARDBLOCKED);
209 }
210 
211 
CheckYard(CSolidObject * yardUnit,const YardMapStatus & mask) const212 inline bool CGroundBlockingObjectMap::CheckYard(CSolidObject* yardUnit, const YardMapStatus& mask) const
213 {
214 	for (int z = yardUnit->mapPos.y; z < yardUnit->mapPos.y + yardUnit->zsize; ++z) {
215 		for (int x = yardUnit->mapPos.x; x < yardUnit->mapPos.x + yardUnit->xsize; ++x) {
216 			if (yardUnit->GetGroundBlockingMaskAtPos(float3(x * SQUARE_SIZE, 0.0f, z * SQUARE_SIZE)) & mask)
217 				if (GroundBlocked(x, z, yardUnit))
218 					return false;
219 		}
220 	}
221 
222 	return true;
223 }
224 
225 
CanOpenYard(CSolidObject * yardUnit) const226 bool CGroundBlockingObjectMap::CanOpenYard(CSolidObject* yardUnit) const
227 {
228 	return CheckYard(yardUnit, YARDMAP_YARDINV);
229 }
230 
CanCloseYard(CSolidObject * yardUnit) const231 bool CGroundBlockingObjectMap::CanCloseYard(CSolidObject* yardUnit) const
232 {
233 	return CheckYard(yardUnit, YARDMAP_YARD);
234 }
235