1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "bladerunner/scene_objects.h"
24
25 #include "bladerunner/bladerunner.h"
26
27 #include "bladerunner/obstacles.h"
28 #include "bladerunner/savefile.h"
29 #include "bladerunner/view.h"
30
31 namespace BladeRunner {
32
SceneObjects(BladeRunnerEngine * vm,View * view)33 SceneObjects::SceneObjects(BladeRunnerEngine *vm, View *view) {
34 _vm = vm;
35 _view = view;
36
37 _count = 0;
38
39 clear();
40 }
41
~SceneObjects()42 SceneObjects::~SceneObjects() {
43 _vm = nullptr;
44 _view = nullptr;
45 _count = 0;
46 }
47
clear()48 void SceneObjects::clear() {
49 for (int i = 0; i < kSceneObjectCount; ++i) {
50 _sceneObjects[i].id = -1;
51 _sceneObjects[i].type = kSceneObjectTypeUnknown;
52 _sceneObjects[i].distanceToCamera = 0.0f;
53 _sceneObjects[i].isPresent = false;
54 _sceneObjects[i].isClickable = false;
55 _sceneObjects[i].isObstacle = false;
56 _sceneObjects[i].unknown1 = 0;
57 _sceneObjects[i].isTarget = false;
58 _sceneObjects[i].isMoving = false;
59 _sceneObjects[i].isRetired = false;
60
61 _sceneObjectsSortedByDistance[i] = -1;
62 }
63 _count = 0;
64 }
65
addActor(int sceneObjectId,const BoundingBox & boundingBox,const Common::Rect & screenRectangle,bool isClickable,bool isMoving,bool isTarget,bool isRetired)66 bool SceneObjects::addActor(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isMoving, bool isTarget, bool isRetired) {
67 return addSceneObject(sceneObjectId, kSceneObjectTypeActor, boundingBox, screenRectangle, isClickable, false, 0, isTarget, isMoving, isRetired);
68 }
69
addObject(int sceneObjectId,const BoundingBox & boundingBox,bool isClickable,bool isObstacle,uint8 unknown1,bool isTarget)70 bool SceneObjects::addObject(int sceneObjectId, const BoundingBox &boundingBox, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget) {
71 Common::Rect rect(-1, -1, -1, -1);
72 return addSceneObject(sceneObjectId, kSceneObjectTypeObject, boundingBox, rect, isClickable, isObstacle, unknown1, isTarget, false, false);
73 }
74
addItem(int sceneObjectId,const BoundingBox & boundingBox,const Common::Rect & screenRectangle,bool isTarget,bool isObstacle)75 bool SceneObjects::addItem(int sceneObjectId, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isTarget, bool isObstacle) {
76 return addSceneObject(sceneObjectId, kSceneObjectTypeItem, boundingBox, screenRectangle, isObstacle, 0, 0, isTarget, 0, 0);
77 }
78
remove(int sceneObjectId)79 bool SceneObjects::remove(int sceneObjectId) {
80 int i = findById(sceneObjectId);
81 if (i == -1) {
82 return false;
83 }
84 _sceneObjects[i].isPresent = false;
85 int j;
86 for (j = 0; j < _count; ++j) {
87 if (_sceneObjectsSortedByDistance[j] == i) {
88 break;
89 }
90 }
91 for (int k = j; k < _count - 1; ++k) {
92 _sceneObjectsSortedByDistance[k] = _sceneObjectsSortedByDistance[k + 1];
93 }
94
95 --_count;
96 return true;
97 }
98
findByXYZ(bool * isClickable,bool * isObstacle,bool * isTarget,Vector3 & position,bool findClickables,bool findObstacles,bool findTargets) const99 int SceneObjects::findByXYZ(bool *isClickable, bool *isObstacle, bool *isTarget, Vector3 &position, bool findClickables, bool findObstacles, bool findTargets) const {
100 *isClickable = false;
101 *isObstacle = false;
102 *isTarget = false;
103
104 for (int i = 0; i < _count; ++i) {
105 assert(_sceneObjectsSortedByDistance[i] < kSceneObjectCount);
106
107 const SceneObject *sceneObject = &_sceneObjects[_sceneObjectsSortedByDistance[i]];
108
109 if ((findClickables && sceneObject->isClickable) ||
110 (findObstacles && sceneObject->isObstacle) ||
111 (findTargets && sceneObject->isTarget)) {
112 BoundingBox boundingBox = sceneObject->boundingBox;
113
114 if (sceneObject->type == kSceneObjectTypeActor) {
115 boundingBox.expand(-4.0, 0.0, -4.0, 4.0, 0.0, 4.0);
116 }
117
118 if (boundingBox.inside(position)) {
119 *isClickable = sceneObject->isClickable;
120 *isObstacle = sceneObject->isObstacle;
121 *isTarget = sceneObject->isTarget;
122
123 return sceneObject->id;
124 }
125 }
126 }
127
128 return -1;
129 }
130
existsOnXZ(int exceptSceneObjectId,float x,float z,bool movingActorIsObstacle,bool standingActorIsObstacle) const131 bool SceneObjects::existsOnXZ(int exceptSceneObjectId, float x, float z, bool movingActorIsObstacle, bool standingActorIsObstacle) const {
132 float xMin = x - 12.5f;
133 float xMax = x + 12.5f;
134 float zMin = z - 12.5f;
135 float zMax = z + 12.5f;
136
137 int count = _count;
138
139 if (count > 0) {
140 for (int i = 0; i < count; ++i) {
141 const SceneObject *sceneObject = &_sceneObjects[_sceneObjectsSortedByDistance[i]];
142 bool isObstacle = false;
143 if (sceneObject->type == kSceneObjectTypeActor) {
144 if (sceneObject->isRetired) {
145 isObstacle = false;
146 } else if (sceneObject->isMoving) {
147 isObstacle = movingActorIsObstacle;
148 } else {
149 isObstacle = standingActorIsObstacle;
150 }
151 } else {
152 isObstacle = sceneObject->isObstacle;
153 }
154
155 if (isObstacle && sceneObject->id != exceptSceneObjectId) {
156 float x1, y1, z1, x2, y2, z2;
157 sceneObject->boundingBox.getXYZ(&x1, &y1, &z1, &x2, &y2, &z2);
158 if (z1 <= zMax && z2 >= zMin && x1 <= xMax && x2 >= xMin) {
159 // if (sceneObject->type == kSceneObjectTypeObject) {
160 // Vector3 a(x1,y1,z1);
161 // Vector3 b(x2,y2,z2);
162 // Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b));
163 // debug("%d: %s (Clk: %s, Trg: %s, Prs: %s, Obs: %s, Mvg: %s), Pos(%02.2f,%02.2f,%02.2f)\n Bbox(%02.2f,%02.2f,%02.2f) ~ (%02.2f,%02.2f,%02.2f)\n",
164 // sceneObject->id - kSceneObjectOffsetObjects,
165 // _vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects).c_str(),
166 // sceneObject->isClickable? "T" : "F",
167 // sceneObject->isTarget? "T" : "F",
168 // sceneObject->isPresent? "T" : "F",
169 // sceneObject->isObstacle? "T" : "F",
170 // sceneObject->isMoving? "T" : "F",
171 // pos.x, pos.y, pos.z,
172 // a.x, a.y, a.z, b.x, b.y, b.z);
173 // }
174 return true;
175 }
176 }
177 }
178 }
179 return false;
180 }
181
findById(int sceneObjectId) const182 int SceneObjects::findById(int sceneObjectId) const {
183 for (int i = 0; i < _count; ++i) {
184 int j = this->_sceneObjectsSortedByDistance[i];
185
186 if (_sceneObjects[j].isPresent && _sceneObjects[j].id == sceneObjectId) {
187 return j;
188 }
189 }
190 return -1;
191 }
192
addSceneObject(int sceneObjectId,SceneObjectType sceneObjectType,const BoundingBox & boundingBox,const Common::Rect & screenRectangle,bool isClickable,bool isObstacle,uint8 unknown1,bool isTarget,bool isMoving,bool isRetired)193 bool SceneObjects::addSceneObject(int sceneObjectId, SceneObjectType sceneObjectType, const BoundingBox &boundingBox, const Common::Rect &screenRectangle, bool isClickable, bool isObstacle, uint8 unknown1, bool isTarget, bool isMoving, bool isRetired) {
194 int index = findEmpty();
195 if (index == -1) {
196 return false;
197 }
198
199 _sceneObjects[index].id = sceneObjectId;
200 _sceneObjects[index].type = sceneObjectType;
201 _sceneObjects[index].isPresent = true;
202 _sceneObjects[index].boundingBox = boundingBox;
203 _sceneObjects[index].screenRectangle = screenRectangle;
204 _sceneObjects[index].isClickable = isClickable;
205 _sceneObjects[index].isObstacle = isObstacle;
206 _sceneObjects[index].unknown1 = unknown1;
207 _sceneObjects[index].isTarget = isTarget;
208 _sceneObjects[index].isMoving = isMoving;
209 _sceneObjects[index].isRetired = isRetired;
210
211 float centerZ = (_sceneObjects[index].boundingBox.getZ0() + _sceneObjects[index].boundingBox.getZ1()) / 2.0f;
212
213 float distanceToCamera = fabs(-centerZ - _view->_cameraPosition.y); // y<->z is intentional, not a bug
214 _sceneObjects[index].distanceToCamera = distanceToCamera;
215
216 // insert according to distance from camera
217 int i;
218 for (i = 0; i < _count; ++i) {
219 if (distanceToCamera < _sceneObjects[_sceneObjectsSortedByDistance[i]].distanceToCamera) {
220 break;
221 }
222 }
223 for (int j = CLIP(_count - 1, 0, kSceneObjectCount - 2); j >= i; --j) {
224 _sceneObjectsSortedByDistance[j + 1] = _sceneObjectsSortedByDistance[j];
225 }
226
227 _sceneObjectsSortedByDistance[i] = index;
228 ++_count;
229 return true;
230 }
231
findEmpty() const232 int SceneObjects::findEmpty() const {
233 for (int i = 0; i < kSceneObjectCount; ++i) {
234 if (!_sceneObjects[i].isPresent)
235 return i;
236 }
237 return -1;
238 }
239
setMoving(int sceneObjectId,bool isMoving)240 void SceneObjects::setMoving(int sceneObjectId, bool isMoving) {
241 int i = findById(sceneObjectId);
242 if (i == -1) {
243 return;
244 }
245 _sceneObjects[i].isMoving = isMoving;
246 }
247
setRetired(int sceneObjectId,bool isRetired)248 void SceneObjects::setRetired(int sceneObjectId, bool isRetired) {
249 int i = findById(sceneObjectId);
250 if (i == -1) {
251 return;
252 }
253 _sceneObjects[i].isRetired = isRetired;
254 }
255
isBetween(float sourceX,float sourceZ,float targetX,float targetZ,int sceneObjectId) const256 bool SceneObjects::isBetween(float sourceX, float sourceZ, float targetX, float targetZ, int sceneObjectId) const {
257 int i = findById(sceneObjectId);
258 if (i == -1) {
259 return false;
260 }
261
262 float objectX1, objectY1, objectZ1, objectX2, objectY2, objectZ2;
263 _sceneObjects[i].boundingBox.getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2);
264
265 Vector2 intersection;
266 return lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ1), Vector2(objectX2, objectZ1), &intersection)
267 || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX2, objectZ1), Vector2(objectX2, objectZ2), &intersection)
268 || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX2, objectZ2), Vector2(objectX1, objectZ2), &intersection)
269 || lineIntersection(Vector2(sourceX, sourceZ), Vector2(targetX, targetZ), Vector2(objectX1, objectZ2), Vector2(objectX1, objectZ1), &intersection);
270 }
271
isObstacleBetween(const Vector3 & source,const Vector3 & target,int exceptSceneObjectId) const272 bool SceneObjects::isObstacleBetween(const Vector3 &source, const Vector3 &target, int exceptSceneObjectId) const {
273 for (int i = 0; i < _count; ++i) {
274 const SceneObject *sceneObject = &_sceneObjects[_sceneObjectsSortedByDistance[i]];
275
276 if (sceneObject->type == kSceneObjectTypeActor || !sceneObject->isObstacle || sceneObject->id == exceptSceneObjectId) {
277 continue;
278 }
279
280 float objectX1, objectY1, objectZ1, objectX2, objectY2, objectZ2;
281 sceneObject->boundingBox.getXYZ(&objectX1, &objectY1, &objectZ1, &objectX2, &objectY2, &objectZ2);
282
283 if (84.0f <= objectY1 - source.y || 72.0f >= objectY2 - source.y) {
284 continue;
285 }
286
287 float xAdjustement = (objectX2 - objectX1) * 0.1f;
288 float zAdjustement = (objectZ2 - objectZ1) * 0.1f;
289
290 objectX1 = objectX1 + xAdjustement;
291 objectZ1 = objectZ1 + zAdjustement;
292 objectX2 = objectX2 - xAdjustement;
293 objectZ2 = objectZ2 - zAdjustement;
294
295 Vector2 intersection;
296 if (lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX1, objectZ1), Vector2(objectX2, objectZ1), &intersection)
297 || lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX2, objectZ1), Vector2(objectX2, objectZ2), &intersection)
298 || lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX2, objectZ2), Vector2(objectX1, objectZ2), &intersection)
299 || lineIntersection(Vector2(source.x, source.z), Vector2(target.x, target.z), Vector2(objectX1, objectZ2), Vector2(objectX1, objectZ1), &intersection)) {
300 return true;
301 }
302 }
303 return false;
304 }
305
setIsClickable(int sceneObjectId,bool isClickable)306 void SceneObjects::setIsClickable(int sceneObjectId, bool isClickable) {
307 int i = findById(sceneObjectId);
308 if (i == -1) {
309 return;
310 }
311 _sceneObjects[i].isClickable = isClickable;
312 }
313
setIsObstacle(int sceneObjectId,bool isObstacle)314 void SceneObjects::setIsObstacle(int sceneObjectId, bool isObstacle) {
315 int i = findById(sceneObjectId);
316 if (i == -1) {
317 return;
318 }
319 _sceneObjects[i].isObstacle = isObstacle;
320 }
321
setIsTarget(int sceneObjectId,bool isTarget)322 void SceneObjects::setIsTarget(int sceneObjectId, bool isTarget) {
323 int i = findById(sceneObjectId);
324 if (i == -1) {
325 return;
326 }
327 _sceneObjects[i].isTarget = isTarget;
328 }
329
updateObstacles()330 void SceneObjects::updateObstacles() {
331 _vm->_obstacles->clear();
332 for (int i = 0; i < _count; ++i) {
333 int index = _sceneObjectsSortedByDistance[i];
334 const SceneObject *sceneObject = &_sceneObjects[index];
335 if (sceneObject->isObstacle) {
336 float x0, y0, z0, x1, y1, z1;
337 sceneObject->boundingBox.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
338 _vm->_obstacles->add(x0, z0, x1, z1);
339 }
340 }
341 _vm->_obstacles->backup();
342 }
343
isEmptyScreenRectangle(int sceneObjectId)344 bool SceneObjects::isEmptyScreenRectangle(int sceneObjectId) {
345 int index = findById(sceneObjectId);
346 if (index != -1) {
347 const SceneObject *sceneObject = &_sceneObjects[index];
348 return sceneObject->screenRectangle.isEmpty();
349 }
350 return true;
351 }
352
compareScreenRectangle(int sceneObjectId,const Common::Rect & rectangle)353 int SceneObjects::compareScreenRectangle(int sceneObjectId, const Common::Rect &rectangle) {
354 int index = findById(sceneObjectId);
355 if (index != -1) {
356 const SceneObject *sceneObject = &_sceneObjects[index];
357 if (sceneObject->screenRectangle == rectangle) {
358 return 0;
359 }
360 }
361 return -1;
362 }
363
resetScreenRectangleAndBbox(int sceneObjectId)364 void SceneObjects::resetScreenRectangleAndBbox(int sceneObjectId) {
365 int index = findById(sceneObjectId);
366 if (index != -1) {
367 SceneObject *sceneObject = &_sceneObjects[index];
368 sceneObject->screenRectangle.left = -1;
369 sceneObject->screenRectangle.top = -1;
370 sceneObject->screenRectangle.right = -1;
371 sceneObject->screenRectangle.bottom = -1;
372 sceneObject->boundingBox.setXYZ(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
373 }
374 }
375
synchScreenRectangle(int sceneObjectId,const Common::Rect & targetScreenRect)376 void SceneObjects::synchScreenRectangle(int sceneObjectId, const Common::Rect &targetScreenRect) {
377 int index = findById(sceneObjectId);
378 if (index != -1) {
379 SceneObject *sceneObject = &_sceneObjects[index];
380 sceneObject->screenRectangle.left = targetScreenRect.left;
381 sceneObject->screenRectangle.top = targetScreenRect.top;
382 sceneObject->screenRectangle.right = targetScreenRect.right;
383 sceneObject->screenRectangle.bottom = targetScreenRect.bottom;
384 }
385 }
386
save(SaveFileWriteStream & f)387 void SceneObjects::save(SaveFileWriteStream &f) {
388 f.writeInt(_count);
389 for (int i = 0; i < kSceneObjectCount; ++i) {
390 f.writeInt(_sceneObjects[i].id);
391 f.writeInt(_sceneObjects[i].type);
392 f.writeBoundingBox(_sceneObjects[i].boundingBox, true);
393 f.writeRect(_sceneObjects[i].screenRectangle);
394 f.writeFloat(_sceneObjects[i].distanceToCamera);
395 f.writeBool(_sceneObjects[i].isPresent);
396 f.writeBool(_sceneObjects[i].isClickable);
397 f.writeBool(_sceneObjects[i].isObstacle);
398 f.writeInt(_sceneObjects[i].unknown1);
399 f.writeBool(_sceneObjects[i].isTarget);
400 f.writeBool(_sceneObjects[i].isMoving);
401 f.writeBool(_sceneObjects[i].isRetired);
402 }
403 for (int i = 0; i < kSceneObjectCount; ++i) {
404 f.writeInt(_sceneObjectsSortedByDistance[i]);
405 }
406 }
407
load(SaveFileReadStream & f)408 void SceneObjects::load(SaveFileReadStream &f) {
409 _count = f.readInt();
410 for (int i = 0; i < kSceneObjectCount; ++i) {
411 _sceneObjects[i].id = f.readInt();
412 _sceneObjects[i].type = (SceneObjectType)f.readInt();
413 _sceneObjects[i].boundingBox = f.readBoundingBox(true);
414 _sceneObjects[i].screenRectangle = f.readRect();
415 _sceneObjects[i].distanceToCamera = f.readFloat();
416 _sceneObjects[i].isPresent = f.readBool();
417 _sceneObjects[i].isClickable = f.readBool();
418 _sceneObjects[i].isObstacle = f.readBool();
419 _sceneObjects[i].unknown1 = f.readInt();
420 _sceneObjects[i].isTarget = f.readBool();
421 _sceneObjects[i].isMoving = f.readBool();
422 _sceneObjects[i].isRetired = f.readBool();
423 }
424 for (int i = 0; i < kSceneObjectCount; ++i) {
425 _sceneObjectsSortedByDistance[i] = f.readInt();
426 }
427 }
428
429 } // End of namespace BladeRunner
430