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/debugger.h"
24 
25 #include "bladerunner/actor.h"
26 #include "bladerunner/bladerunner.h"
27 #include "bladerunner/boundingbox.h"
28 #include "bladerunner/combat.h"
29 #include "bladerunner/font.h"
30 #include "bladerunner/fog.h"
31 #include "bladerunner/game_constants.h"
32 #include "bladerunner/game_flags.h"
33 #include "bladerunner/game_info.h"
34 #include "bladerunner/light.h"
35 #include "bladerunner/lights.h"
36 #include "bladerunner/regions.h"
37 #include "bladerunner/savefile.h"
38 #include "bladerunner/scene.h"
39 #include "bladerunner/scene_objects.h"
40 #include "bladerunner/items.h"
41 #include "bladerunner/item_pickup.h"
42 #include "bladerunner/screen_effects.h"
43 #include "bladerunner/settings.h"
44 #include "bladerunner/set.h"
45 #include "bladerunner/set_effects.h"
46 #include "bladerunner/text_resource.h"
47 #include "bladerunner/time.h"
48 #include "bladerunner/vector.h"
49 #include "bladerunner/view.h"
50 #include "bladerunner/vqa_decoder.h"
51 #include "bladerunner/vqa_player.h"
52 #include "bladerunner/waypoints.h"
53 #include "bladerunner/zbuffer.h"
54 #include "bladerunner/chapters.h"
55 #include "bladerunner/ui/kia.h"
56 #include "bladerunner/ui/esper.h"
57 #include "bladerunner/ui/spinner.h"
58 #include "bladerunner/ui/elevator.h"
59 #include "bladerunner/ui/vk.h"
60 #include "bladerunner/ui/scores.h"
61 #include "bladerunner/script/vk_script.h"
62 #include "bladerunner/overlays.h"
63 #include "bladerunner/subtitles.h"
64 
65 
66 #include "common/debug.h"
67 #include "common/str.h"
68 
69 #include "graphics/surface.h"
70 
71 namespace BladeRunner {
72 
Debugger(BladeRunnerEngine * vm)73 Debugger::Debugger(BladeRunnerEngine *vm) : GUI::Debugger() {
74 	_vm = vm;
75 
76 	_isDebuggerOverlay = false;
77 
78 	_viewActorsToggle = false;
79 	_view3dObjectsToggle = false;
80 	_viewItemsToggle = false;
81 	_viewLights = false;
82 	_viewFogs = false;
83 	_viewScreenEffects = false;
84 	_viewObstacles = false;
85 	_viewRegionsNormalToggle = false;
86 	_viewRegionsExitsToggle = false;
87 	_viewUI = false;
88 	_viewWaypointsNormalToggle = false;
89 	_viewWaypointsFleeToggle = false;
90 	_viewWaypointsCoverToggle = false;
91 	_viewWalkboxes = false;
92 	_viewZBuffer = false;
93 
94 	_specificActorsDrawn = false;
95 	_specific3dObjectsDrawn = false;
96 	_specificItemsDrawn = false;
97 	_specificEffectsDrawn = false;
98 	_specificLightsDrawn = false;
99 	_specificFogsDrawn = false;
100 	_specificRegionNormalDrawn = false;
101 	_specificRegionExitsDrawn = false;
102 	_specificWaypointNormalDrawn = false;
103 	_specificWaypointFleeDrawn = false;
104 	_specificWaypointCoverDrawn = false;
105 	_specificWalkboxesDrawn = false;
106 
107 	_playFullVk = false;
108 	_showStatsVk = false;
109 	_showMazeScore = false;
110 	_showMouseClickInfo = false;
111 
112 	registerCmd("anim", WRAP_METHOD(Debugger, cmdAnimation));
113 	registerCmd("health", WRAP_METHOD(Debugger, cmdHealth));
114 	registerCmd("draw", WRAP_METHOD(Debugger, cmdDraw));
115 	registerCmd("list", WRAP_METHOD(Debugger, cmdList));
116 	registerCmd("flag", WRAP_METHOD(Debugger, cmdFlag));
117 	registerCmd("goal", WRAP_METHOD(Debugger, cmdGoal));
118 	registerCmd("loop", WRAP_METHOD(Debugger, cmdLoop));
119 	registerCmd("pos", WRAP_METHOD(Debugger, cmdPosition));
120 	registerCmd("say", WRAP_METHOD(Debugger, cmdSay));
121 	registerCmd("scene", WRAP_METHOD(Debugger, cmdScene));
122 	registerCmd("var", WRAP_METHOD(Debugger, cmdVariable));
123 	registerCmd("clue", WRAP_METHOD(Debugger, cmdClue));
124 	registerCmd("timer", WRAP_METHOD(Debugger, cmdTimer));
125 	registerCmd("friend", WRAP_METHOD(Debugger, cmdFriend));
126 	registerCmd("load", WRAP_METHOD(Debugger, cmdLoad));
127 	registerCmd("save", WRAP_METHOD(Debugger, cmdSave));
128 	registerCmd("overlay", WRAP_METHOD(Debugger, cmdOverlay));
129 	registerCmd("subtitle", WRAP_METHOD(Debugger, cmdSubtitle));
130 	registerCmd("vk", WRAP_METHOD(Debugger, cmdVk));
131 	registerCmd("mazescore", WRAP_METHOD(Debugger, cmdMazeScore));
132 	registerCmd("object", WRAP_METHOD(Debugger, cmdObject));
133 	registerCmd("item", WRAP_METHOD(Debugger, cmdItem));
134 	registerCmd("region", WRAP_METHOD(Debugger, cmdRegion));
135 	registerCmd("click", WRAP_METHOD(Debugger, cmdClick));
136 	registerCmd("difficulty", WRAP_METHOD(Debugger, cmdDifficulty));
137 #if BLADERUNNER_ORIGINAL_BUGS
138 #else
139 	registerCmd("effect", WRAP_METHOD(Debugger, cmdEffect));
140 #endif // BLADERUNNER_ORIGINAL_BUGS
141 }
142 
~Debugger()143 Debugger::~Debugger() {
144 	if (!_specificDrawnObjectsList.empty()) {
145 		_specificDrawnObjectsList.clear();
146 	}
147 }
148 
cmdAnimation(int argc,const char ** argv)149 bool Debugger::cmdAnimation(int argc, const char **argv) {
150 	if (argc != 2 && argc != 4) {
151 		debugPrintf("Get or set animation mode of the actor.\n");
152 		debugPrintf("Usage: %s <actorId> [<animationMode> <showDamageAnimationWhenMoving>]\n", argv[0]);
153 		return true;
154 	}
155 
156 	int actorId = atoi(argv[1]);
157 
158 	Actor *actor = nullptr;
159 	if (actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) {
160 		actor = _vm->_actors[actorId];
161 	}
162 
163 	if (actor == nullptr) {
164 		debugPrintf("Unknown actor %i\n", actorId);
165 		return true;
166 	}
167 
168 	if (argc == 4) {
169 		int animationMode = atoi(argv[2]);
170 		int showDmgWhenMoving = atoi(argv[3]);
171 		actor->setFlagDamageAnimIfMoving(showDmgWhenMoving!=0);
172 		actor->changeAnimationMode(animationMode, true);
173 		debugPrintf("actorAnimationMode(%i) = %i, showDamageWhenMoving = %i\n", actorId, animationMode, actor->getFlagDamageAnimIfMoving());
174 		return false;
175 	}
176 
177 	debugPrintf("actorAnimationMode(%i) = %i, showDamageWhenMoving = %i, inCombat = %i\n", actorId, actor->getAnimationMode(), actor->getFlagDamageAnimIfMoving(), actor->inCombat());
178 	return true;
179 }
180 
cmdHealth(int argc,const char ** argv)181 bool Debugger::cmdHealth(int argc, const char **argv) {
182 	if (argc != 2 && argc != 4) {
183 		debugPrintf("Get or set health for the actor.\n");
184 		debugPrintf("Usage: %s <actorId> [<health> <max health>]\n", argv[0]);
185 		return true;
186 	}
187 
188 	int actorId = atoi(argv[1]);
189 
190 	Actor *actor = nullptr;
191 	if (actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) {
192 		actor = _vm->_actors[actorId];
193 	}
194 
195 	if (actor == nullptr) {
196 		debugPrintf("Unknown actor %i\n", actorId);
197 		return true;
198 	}
199 
200 	if (argc == 4) {
201 		int currHealth = atoi(argv[2]);
202 		int maxHealth = atoi(argv[3]);
203 		currHealth = CLIP(currHealth, 0, 100);
204 		maxHealth = CLIP(maxHealth, 0, 100);
205 		if (currHealth > maxHealth) {
206 			debugPrintf("Actor's current health cannot be greater than their max health\n");
207 			return true;
208 		}
209 		actor->setHealth(currHealth, maxHealth);
210 	}
211 
212 	debugPrintf("actor health(%i) = %i, max: %i\n", actorId, actor->getCurrentHP(), actor->getMaxHP());
213 	return true;
214 }
215 
cmdDraw(int argc,const char ** argv)216 bool Debugger::cmdDraw(int argc, const char **argv) {
217 	bool invalidSyntax = false;
218 
219 	if (argc != 2 && argc != 3) {
220 		invalidSyntax = true;
221 	} else {
222 		Common::String arg = argv[1];
223 
224 		DebuggerDrawnObject dbgDrawnObj;
225 		dbgDrawnObj.type = debuggerObjTypeUndefined;
226 
227 		if (argc == 3) {
228 			int specificObjectId = atoi(argv[2]);
229 			dbgDrawnObj.objId = specificObjectId;
230 			dbgDrawnObj.sceneId = _vm->_scene->getSceneId();
231 			dbgDrawnObj.setId = _vm->_scene->getSetId();
232 		}
233 
234 		if (arg == "allobj") {
235 			if (_viewActorsToggle && _view3dObjectsToggle && _viewItemsToggle) {
236 				_viewActorsToggle = false;
237 				_view3dObjectsToggle  = false;
238 				_viewItemsToggle = false;
239 			} else {
240 				_viewActorsToggle = true;
241 				_view3dObjectsToggle  = true;
242 				_viewItemsToggle = true;
243 			}
244 			debugPrintf("Drawing all scene objects (actors, 3d objects, items) = %i\n", _viewActorsToggle && _view3dObjectsToggle && _viewItemsToggle);
245 		} else if (arg == "act") {
246 			if (argc == 2) {
247 				_viewActorsToggle = !_viewActorsToggle;
248 				debugPrintf("Drawing all actors in scene = %s\n", _viewActorsToggle? "true" : "false");
249 			} else {
250 				_viewActorsToggle = false;
251 				dbgDrawnObj.setId = -1;
252 				dbgDrawnObj.sceneId = -1;
253 				dbgDrawnObj.type = debuggerObjTypeActor;
254 			}
255 		} else if (arg == "obj") {
256 			if (argc == 2) {
257 				_view3dObjectsToggle = !_view3dObjectsToggle;
258 				debugPrintf("Drawing all 3d objects in scene = %s\n", _view3dObjectsToggle? "true" : "false");
259 			} else {
260 				_view3dObjectsToggle = false;
261 				dbgDrawnObj.type = debuggerObjType3dObject;
262 			}
263 		} else if (arg == "item") {
264 			if (argc == 2) {
265 				_viewItemsToggle = !_viewItemsToggle;
266 				debugPrintf("Drawing all items in scene = %s\n", _viewItemsToggle? "true" : "false");
267 			} else {
268 				_viewItemsToggle = false;
269 				dbgDrawnObj.setId = -1;
270 				dbgDrawnObj.sceneId = -1;
271 				dbgDrawnObj.type = debuggerObjTypeItem;
272 			}
273 		} else if (arg == "eff") {
274 			if (argc == 2) {
275 				_viewScreenEffects = !_viewScreenEffects;
276 				debugPrintf("Drawing all screen effects = %s\n", _viewScreenEffects? "true" : "false");
277 			} else {
278 				_viewScreenEffects = false;
279 				dbgDrawnObj.type = debuggerObjTypeEffect;
280 			}
281 		} else if (arg == "fog") {
282 			if (argc == 2) {
283 				_viewFogs = !_viewFogs;
284 				debugPrintf("Drawing all fogs = %s\n", _viewFogs? "true" : "false");
285 			} else {
286 				_viewFogs = false;
287 				dbgDrawnObj.type = debuggerObjTypeFog;
288 			}
289 		} else if (arg == "lit") {
290 			if (argc == 2) {
291 				_viewLights = !_viewLights;
292 				debugPrintf("Drawing all lights = %s\n", _viewLights? "true" : "false");
293 			} else {
294 				_viewLights = false;
295 				dbgDrawnObj.type = debuggerObjTypeLight;
296 			}
297 		} else if (arg == "allreg") {
298 			if (_viewRegionsNormalToggle && _viewRegionsExitsToggle) {
299 				_viewRegionsNormalToggle = false;
300 				_viewRegionsExitsToggle  = false;
301 			} else {
302 				_viewRegionsNormalToggle = true;
303 				_viewRegionsExitsToggle  = true;
304 			}
305 			debugPrintf("Drawing all scene regions (regular, exits) = %s\n", (_viewRegionsNormalToggle && _viewRegionsExitsToggle)? "true" : "false");
306 		} else if (arg == "regnorm") {
307 			if (argc == 2) {
308 				_viewRegionsNormalToggle = !_viewRegionsNormalToggle;
309 				debugPrintf("Drawing all normal regions = %s\n", _viewRegionsNormalToggle? "true" : "false");
310 			} else {
311 				_viewRegionsNormalToggle = false;
312 				dbgDrawnObj.type = debuggerObjTypeRegionNormal;
313 			}
314 		} else if (arg == "regexit") {
315 			if (argc == 2) {
316 				_viewRegionsExitsToggle = !_viewRegionsExitsToggle;
317 				debugPrintf("Drawing all exit regions = %s\n", _viewRegionsExitsToggle? "true" : "false");
318 			} else {
319 				_viewRegionsExitsToggle = false;
320 				dbgDrawnObj.type = debuggerObjTypeRegionExit;
321 			}
322 		} else if (arg == "obstacles") {
323 			_viewObstacles = !_viewObstacles;
324 			debugPrintf("Drawing obstacles = %s\n", _viewObstacles? "true" : "false");
325 		} else if (arg == "ui") {
326 			_viewUI = !_viewUI;
327 			debugPrintf("Drawing all UI elements = %s\n", _viewUI? "true" : "false");
328 		} else if (arg == "allway") {
329 			if (_viewWaypointsNormalToggle && _viewWaypointsFleeToggle && _viewWaypointsCoverToggle) {
330 				_viewWaypointsNormalToggle = false;
331 				_viewWaypointsFleeToggle  = false;
332 				_viewWaypointsCoverToggle = false;
333 			} else {
334 				_viewWaypointsNormalToggle = true;
335 				_viewWaypointsFleeToggle = true;
336 				_viewWaypointsCoverToggle = true;
337 			}
338 			debugPrintf("Drawing all waypoints (regular, cover, flee) = %s\n", (_viewWaypointsNormalToggle && _viewWaypointsFleeToggle && _viewWaypointsCoverToggle)? "true" : "false");
339 		} else if (arg == "waynorm") {
340 			if (argc == 2) {
341 				_viewWaypointsNormalToggle = !_viewWaypointsNormalToggle;
342 				debugPrintf("Drawing all normal waypoints = %s\n", _viewWaypointsNormalToggle? "true" : "false");
343 			} else {
344 				_viewWaypointsNormalToggle = false;
345 				dbgDrawnObj.setId = -1;
346 				dbgDrawnObj.sceneId = -1;
347 				dbgDrawnObj.type = debuggerObjTypeWaypointNorm;
348 			}
349 		} else if (arg == "wayflee") {
350 			if (argc == 2) {
351 				_viewWaypointsFleeToggle = !_viewWaypointsFleeToggle;
352 				debugPrintf("Drawing all flee waypoints = %s\n", _viewWaypointsFleeToggle? "true" : "false");
353 			} else {
354 				_viewWaypointsFleeToggle = false;
355 				dbgDrawnObj.setId = -1;
356 				dbgDrawnObj.sceneId = -1;
357 				dbgDrawnObj.type = debuggerObjTypeWaypoingFlee;
358 			}
359 		} else if (arg == "waycov") {
360 			if (argc == 2) {
361 				_viewWaypointsCoverToggle = !_viewWaypointsCoverToggle;
362 				debugPrintf("Drawing all cover waypoints = %s\n", _viewWaypointsCoverToggle? "true" : "false");
363 			} else {
364 				_viewWaypointsCoverToggle = false;
365 				dbgDrawnObj.setId = -1;
366 				dbgDrawnObj.sceneId = -1;
367 				dbgDrawnObj.type = debuggerObjTypeWaypointCover;
368 			}
369 		} else if (arg == "walk") {
370 			if (argc == 2) {
371 				_viewWalkboxes = !_viewWalkboxes;
372 				debugPrintf("Drawing all walk boxes = %s\n", _viewWalkboxes? "true" : "false");
373 			} else {
374 				_viewWalkboxes = false;
375 				dbgDrawnObj.type = debuggerObjTypeWalkbox;
376 			}
377 		} else if (arg == "zbuf") {
378 			_viewZBuffer = !_viewZBuffer;
379 			debugPrintf("Drawing Z buffer = %s\n", _viewZBuffer? "true" : "false");
380 		} else if (arg == "reset") {
381 
382 			if (!_specificDrawnObjectsList.empty()) {
383 				_specificDrawnObjectsList.clear();
384 			}
385 
386 			_viewActorsToggle = false;
387 			_view3dObjectsToggle = false;
388 			_viewItemsToggle = false;
389 			_viewObstacles = false;
390 			_viewRegionsNormalToggle = false;
391 			_viewRegionsExitsToggle = false;
392 			_viewScreenEffects = false;
393 			_viewFogs = false;
394 			_viewLights = false;
395 			_viewUI = false;
396 			_viewWaypointsNormalToggle = false;
397 			_viewWaypointsFleeToggle = false;
398 			_viewWaypointsCoverToggle = false;
399 			_viewWalkboxes = false;
400 			_viewZBuffer = false;
401 
402 			debugPrintf("Drawing all scene objects (actors, 3d objects, items) = %s\n", (_viewActorsToggle && _view3dObjectsToggle && _viewItemsToggle)? "true" : "false");
403 			debugPrintf("Drawing scene actors = %s\n", _viewActorsToggle? "true" : "false");
404 			debugPrintf("Drawing scene 3d objects = %s\n", _view3dObjectsToggle? "true" : "false");
405 			debugPrintf("Drawing scene items = %s\n", _viewItemsToggle? "true" : "false");
406 			debugPrintf("Drawing obstacles = %s\n", _viewObstacles? "true" : "false");
407 			debugPrintf("Drawing all regions (regular, exits) = %s\n", (_viewRegionsNormalToggle && _viewRegionsExitsToggle)? "true" : "false");
408 			debugPrintf("Drawing regular regions = %s\n",  _viewRegionsNormalToggle? "true" : "false");
409 			debugPrintf("Drawing exit regions = %s\n",  _viewRegionsExitsToggle? "true" : "false");
410 			debugPrintf("Drawing screen effects = %s\n", _viewScreenEffects? "true" : "false");
411 			debugPrintf("Drawing fogs = %s\n", _viewFogs? "true" : "false");
412 			debugPrintf("Drawing lights = %s\n", _viewLights? "true" : "false");
413 			debugPrintf("Drawing UI elements = %s\n", _viewUI? "true" : "false");
414 			debugPrintf("Drawing all waypoints (regular, cover, flee) = %s\n", (_viewWaypointsNormalToggle && _viewWaypointsFleeToggle && _viewWaypointsCoverToggle)? "true" : "false");
415 			debugPrintf("Drawing regular waypoints = %s\n", _viewWaypointsNormalToggle? "true" : "false");
416 			debugPrintf("Drawing flee waypoints = %s\n", _viewWaypointsFleeToggle? "true" : "false");
417 			debugPrintf("Drawing cover waypoints = %s\n", _viewWaypointsCoverToggle? "true" : "false");
418 			debugPrintf("Drawing walkboxes = %s\n", _viewWalkboxes? "true" : "false");
419 			debugPrintf("Drawing Z buffer = %s\n", _viewZBuffer? "true" : "false");
420 		} else {
421 			invalidSyntax = true;
422 		}
423 
424 		if (!invalidSyntax) {
425 			if (dbgDrawnObj.type != debuggerObjTypeUndefined) {
426 				toggleObjectInDbgDrawList(dbgDrawnObj);
427 			}
428 			updateTogglesForDbgDrawListInCurrentSetAndScene();
429 		}
430 	}
431 
432 	if (invalidSyntax) {
433 		debugPrintf("Enables debug rendering of actors, screen effect, fogs, lights, scene objects\nobstacles, regions, ui elements, walk boxes, waypoints, zbuffer or disables debug rendering.\n");
434 		debugPrintf("Usage 1: %s (allobj | obstacles | allreg | ui | allway | zbuf | reset)\n", argv[0]);
435 		debugPrintf("Usage 2a: %s (act | obj | item | regnorm | regexit | waynorm | wayflee | waycov) [<id>]\n", argv[0]);
436 		debugPrintf("Usage 2b: %s (eff | fog | lit | walk) [<id>]\n", argv[0]);
437 	}
438 	return true;
439 }
440 
cmdFlag(int argc,const char ** argv)441 bool Debugger::cmdFlag(int argc, const char **argv) {
442 	if (argc != 2 && argc != 3) {
443 		debugPrintf("Get or set game flag (boolean value).\n");
444 		debugPrintf("Usage: %s <id> [<value>]\n", argv[0]);
445 		return true;
446 	}
447 
448 	int flag = atoi(argv[1]);
449 	int flagCount = _vm->_gameInfo->getFlagCount();
450 	if (flag > 0 && flag < flagCount) {
451 		if (argc == 3) {
452 			int value = atoi(argv[2]);
453 			if (value == 0) {
454 				_vm->_gameFlags->reset(flag);
455 			} else {
456 				_vm->_gameFlags->set(flag);
457 			}
458 		}
459 		debugPrintf("flag(%i) = %i\n", flag, _vm->_gameFlags->query(flag));
460 	} else {
461 		debugPrintf("Flag id must be between 0 and %i\n", flagCount - 1);
462 	}
463 
464 	return true;
465 }
466 
cmdGoal(int argc,const char ** argv)467 bool Debugger::cmdGoal(int argc, const char **argv) {
468 	if (argc != 2 && argc != 3) {
469 		debugPrintf("Get or set goal of the actor.\n");
470 		debugPrintf("Usage: %s <actorId> [<goal>]\n", argv[0]);
471 		return true;
472 	}
473 
474 	int actorId = atoi(argv[1]);
475 
476 	Actor *actor = nullptr;
477 	if (actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) {
478 		actor = _vm->_actors[actorId];
479 	}
480 
481 	if (actor == nullptr) {
482 		debugPrintf("Unknown actor %i\n", actorId);
483 		return true;
484 	}
485 
486 	if (argc == 3) {
487 		int goal = atoi(argv[2]);
488 		debugPrintf("actorGoal(%i) = %i\n", actorId, goal);
489 		actor->setGoal(goal);
490 		return false;
491 	}
492 
493 	debugPrintf("actorGoal(%i) = %i\n", actorId, actor->getGoal());
494 	return true;
495 }
496 
cmdLoop(int argc,const char ** argv)497 bool Debugger::cmdLoop(int argc, const char **argv) {
498 	if (argc != 1 && argc != 2) {
499 		debugPrintf("Show scene loops or play scene loop.\n");
500 		debugPrintf("Usage: %s [<loopId>]\n", argv[0]);
501 		return true;
502 	}
503 
504 	VQADecoder::LoopInfo &loopInfo = _vm->_scene->_vqaPlayer->_decoder._loopInfo;
505 	if (argc == 1) {
506 		debugPrintf("id start  end name\n");
507 		for (int i = 0; i < loopInfo.loopCount; ++i) {
508 			debugPrintf("%2d  %4d %4d %s\n", i, loopInfo.loops[i].begin, loopInfo.loops[i].end, loopInfo.loops[i].name.c_str());
509 		}
510 		return true;
511 	}
512 
513 	int loopId = atoi(argv[1]);
514 	if (loopId >= 0 && loopId < loopInfo.loopCount) {
515 		_vm->_scene->loopStartSpecial(kSceneLoopModeOnce, loopId, false);
516 		return false;
517 	} else {
518 		debugPrintf("Unknown loop %i\n", loopId);
519 		return true;
520 	}
521 }
522 
cmdPosition(int argc,const char ** argv)523 bool Debugger::cmdPosition(int argc, const char **argv) {
524 	if (argc != 2 && argc != 3 && argc != 7) {
525 		debugPrintf("Get or set position of the actor.\n");
526 		debugPrintf("Usage: %s <actorId> [(<setId> <x> <y> <z> <facing>) | <otherActorId>]\n", argv[0]);
527 		return true;
528 	}
529 
530 	int actorId = atoi(argv[1]);
531 
532 	Actor *actor = nullptr;
533 	if (actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) {
534 		actor = _vm->_actors[actorId];
535 	}
536 
537 	if (actor == nullptr) {
538 		debugPrintf("Unknown actor %i\n", actorId);
539 		return true;
540 	}
541 
542 	if (argc == 2) {
543 		debugPrintf("actorSet(%i) = %i\n", actorId, actor->getSetId());
544 		debugPrintf("actorX(%i) = %f\n", actorId, actor->getX());
545 		debugPrintf("actorY(%i) = %f\n", actorId, actor->getY());
546 		debugPrintf("actorZ(%i) = %f\n", actorId, actor->getZ());
547 		debugPrintf("actorFacing(%i) = %i\n", actorId, actor->getFacing());
548 	}
549 
550 	if (argc == 3) {
551 		int otherActorId = atoi(argv[2]);
552 		Actor *otherActor = nullptr;
553 		if (otherActorId >= 0 && otherActorId < (int)_vm->_gameInfo->getActorCount()) {
554 			otherActor = _vm->_actors[otherActorId];
555 		}
556 
557 		if (otherActor == nullptr) {
558 			debugPrintf("Unknown actor %i\n", otherActorId);
559 			return true;
560 		}
561 
562 		Vector3 position = otherActor->getXYZ();
563 		actor->setSetId(otherActor->getSetId());
564 		actor->setAtXYZ(position, otherActor->getFacing());
565 	}
566 
567 	if (argc == 7) {
568 		int setId = atoi(argv[2]);
569 		Vector3 position(atof(argv[3]), atof(argv[4]), atof(argv[5]));
570 		int facing = atoi(argv[6]);
571 
572 		actor->setSetId(setId);
573 		actor->setAtXYZ(position, facing);
574 	}
575 	return true;
576 }
577 
cmdSay(int argc,const char ** argv)578 bool Debugger::cmdSay(int argc, const char **argv) {
579 	if (argc != 3) {
580 		debugPrintf("Actor will say specified line.\n");
581 		debugPrintf("Usage: %s <actorId> <sentenceId>\n", argv[0]);
582 		return true;
583 	}
584 
585 	int actorId = atoi(argv[1]);
586 	int sentenceId = atoi(argv[2]);
587 
588 	Actor *actor = nullptr;
589 	if ((actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) || (actorId == kActorVoiceOver)) {
590 		actor = _vm->_actors[actorId];
591 	}
592 
593 	if (actor == nullptr) {
594 		debugPrintf("Unknown actor %i\n", actorId);
595 		return true;
596 	}
597 
598 	actor->speechPlay(sentenceId, true);
599 	return false;
600 }
601 
602 const struct SceneList {
603 	int chapter;
604 	const char *name;
605 	int set;
606 	int scene;
607 } sceneList[] = {
608 	{ 1, "CT01", 4, 13 },    { 1, "CT02", 27, 14 },  { 1, "CT03", 5, 15 },    { 1, "CT04", 5, 16 },
609 	{ 1, "CT05", 28, 17 },   { 1, "CT06", 29, 18 },  { 1, "CT07", 30, 19 },   { 1, "CT12", 4, 24 },
610 	{ 1, "MA01", 49, 48 },   { 1, "MA02", 10, 49 },  { 1, "MA04", 10, 50 },   { 1, "MA04", 50, 50 },
611 	{ 1, "MA05", 51, 51 },   { 1, "MA06", 52, 52 },  { 1, "MA07", 53, 53 },   { 1, "PS01", 61, 65 },
612 	{ 1, "PS02", 62, 66 },   { 1, "PS03", 63, 67 },  { 1, "PS04", 64, 68 },   { 1, "PS05", 15, 69 },
613 	{ 1, "PS06", 65, 70 },   { 1, "PS07", 66, 71 },  { 1, "PS09", 67, 72 },   { 1, "PS10", 14, 73 },
614 	{ 1, "PS11", 14, 74 },   { 1, "PS12", 14, 75 },  { 1, "PS13", 14, 76 },   { 1, "PS14", 68, 77 },
615 	{ 1, "PS15", 101, 119 }, { 1, "RC01", 69, 78 },  { 1, "RC02", 16, 79 },   { 1, "RC51", 16, 107 },
616 
617 	{ 2, "AR01", 0, 0 },     { 2, "AR02", 0, 1 },    { 2, "BB01", 20, 2 },    { 2, "BB02", 1, 3 },
618 	{ 2, "BB03", 21, 4 },    { 2, "BB04", 1, 5 },    { 2, "BB05", 22, 6 },    { 2, "BB06", 1, 7 },
619 	{ 2, "BB06", 2, 7 },     { 2, "BB07", 2, 8 },    { 2, "BB07", 3, 8 },     { 2, "BB08", 23, 9 },
620 	{ 2, "BB09", 24, 10 },   { 2, "BB10", 25, 11 },  { 2, "BB11", 26, 12 },   { 2, "BB12", 102, 120 },
621 	{ 2, "BB51", 1, 104 },   { 2, "CT01", 4, 13 },   { 2, "CT02", 27, 14 },   { 2, "CT03", 5, 15 },
622 	{ 2, "CT04", 5, 16 },    { 2, "CT05", 28, 17 },  { 2, "CT06", 29, 18 },   { 2, "CT08", 6, 20 },
623 	{ 2, "CT09", 31, 21 },   { 2, "CT10", 32, 22 },  { 2, "CT11", 33, 23 },   { 2, "CT12", 4, 24 },
624 	{ 2, "CT51", 6, 105 },   { 2, "DR01", 7, 25 },   { 2, "DR02", 7, 26 },    { 2, "DR03", 34, 27 },
625 	{ 2, "DR04", 7, 28 },    { 2, "DR05", 35, 29 },  { 2, "DR06", 36, 30 },   { 2, "HC01", 8, 31 },
626 	{ 2, "HC02", 8, 32 },    { 2, "HC03", 8, 33 },   { 2, "HC04", 8, 106 },   { 2, "HF01", 37, 34 },
627 	{ 2, "HF02", 38, 35 },   { 2, "HF03", 39, 36 },  { 2, "HF04", 40, 37 },   { 2, "HF05", 41, 38 },
628 	{ 2, "HF06", 42, 39 },   { 2, "MA01", 49, 48 },  { 2, "MA02", 10, 49 },   { 2, "MA04", 10, 50 },
629 	{ 2, "MA04", 50, 50 },   { 2, "MA05", 51, 51 },  { 2, "MA06", 52, 52 },   { 2, "MA07", 53, 53 },
630 	{ 2, "NR01", 54, 54 },   { 2, "NR02", 11, 55 },  { 2, "NR03", 55, 56 },   { 2, "NR04", 12, 57 },
631 	{ 2, "NR05", 13, 58 },   { 2, "NR06", 56, 59 },  { 2, "NR07", 57, 60 },   { 2, "NR08", 13, 61 },
632 	{ 2, "NR09", 58, 62 },   { 2, "NR10", 59, 63 },  { 2, "NR11", 60, 64 },   { 2, "PS01", 61, 65 },
633 	{ 2, "PS02", 62, 66 },   { 2, "PS03", 63, 67 },  { 2, "PS04", 64, 68 },   { 2, "PS05", 15, 69 },
634 	{ 2, "PS06", 65, 70 },   { 2, "PS07", 66, 71 },  { 2, "PS09", 67, 72 },   { 2, "PS10", 14, 73 },
635 	{ 2, "PS11", 14, 74 },   { 2, "PS12", 14, 75 },  { 2, "PS13", 14, 76 },   { 2, "PS14", 68, 77 },
636 	{ 2, "PS15", 101, 119 }, { 2, "RC01", 69, 78 },  { 2, "RC03", 70, 80 },   { 2, "RC04", 71, 81 },
637 	{ 2, "TB02", 17, 82 },   { 2, "TB05", 72, 84 },  { 2, "TB06", 73, 85 },   { 2, "TB07", 18, 108 },
638 	{ 2, "UG01", 74, 86 },   { 2, "UG02", 75, 87 },  { 2, "UG03", 76, 88 },   { 2, "UG04", 77, 89 },
639 	{ 2, "UG06", 79, 91 },   { 2, "UG10", 83, 95 },
640 
641 	{ 4, "AR01", 0, 0 },     { 4, "AR02", 0, 1 },    { 4, "BB01", 20, 2 },    { 4, "BB02", 1, 3 },
642 	{ 4, "BB03", 21, 4 },    { 4, "BB04", 1, 5 },    { 4, "BB51", 1, 104 },   { 4, "CT01", 4, 13 },
643 	{ 4, "CT02", 27, 14 },   { 4, "CT03", 5, 15 },   { 4, "CT04", 5, 16 },    { 4, "CT05", 28, 17 },
644 	{ 4, "CT06", 29, 18 },   { 4, "CT08", 6, 20 },   { 4, "CT09", 31, 21 },   { 4, "CT10", 32, 22 },
645 	{ 4, "CT11", 33, 23 },   { 4, "CT12", 4, 24 },   { 4, "CT51", 6, 105 },   { 4, "DR01", 7, 25 },
646 	{ 4, "DR02", 7, 26 },    { 4, "DR03", 34, 27 },  { 4, "DR04", 7, 28 },    { 4, "DR05", 35, 29 },
647 	{ 4, "DR06", 36, 30 },   { 4, "HC01", 8, 31 },   { 4, "HC02", 8, 32 },    { 4, "HC03", 8, 33 },
648 	{ 4, "HC04", 8, 106 },   { 4, "HF01", 37, 34 },  { 4, "HF02", 38, 35 },   { 4, "HF03", 39, 36 },
649 	{ 4, "HF04", 40, 37 },   { 4, "HF05", 41, 38 },  { 4, "HF06", 42, 39 },   { 4, "HF07", 43, 40 },
650 	{ 4, "KP01", 44, 41 },   { 4, "KP02", 45, 42 },  { 4, "KP03", 46, 43 },   { 4, "KP04", 47, 44 },
651 	{ 4, "KP05", 9, 45 },    { 4, "KP06", 9, 46 },   { 4, "KP07", 48, 47 },   { 4, "MA02", 10, 49 },
652 	{ 4, "MA04", 10, 50 },   { 4, "MA04", 50, 50 },  { 4, "MA05", 51, 51 },   { 4, "MA06", 52, 52 },
653 	{ 4, "MA07", 53, 53 },   { 4, "NR01", 54, 54 },  { 4, "NR02", 11, 55 },   { 4, "NR03", 55, 56 },
654 	{ 4, "NR04", 12, 57 },   { 4, "NR05", 13, 58 },  { 4, "NR06", 56, 59 },   { 4, "NR07", 57, 60 },
655 	{ 4, "NR08", 13, 61 },   { 4, "NR09", 58, 62 },  { 4, "NR10", 59, 63 },   { 4, "NR11", 60, 64 },
656 	{ 4, "PS09", 67, 72 },   { 4, "PS14", 68, 77 },  { 4, "RC01", 69, 78 },   { 4, "RC02", 16, 89 },
657 	{ 4, "RC03", 70, 80 },   { 4, "RC04", 71, 81 },  { 4, "RC51", 16, 107 },  { 4, "TB02", 17, 82 },
658 	{ 4, "TB03", 17, 83 },   { 4, "TB07", 18, 108 }, { 4, "UG01", 74, 86 },   { 4, "UG02", 75, 87 },
659 	{ 4, "UG03", 76, 88 },   { 4, "UG04", 77, 89 },  { 4, "UG05", 78, 90 },   { 4, "UG06", 79, 91 },
660 	{ 4, "UG07", 80, 92 },   { 4, "UG08", 81, 93 },  { 4, "UG09", 82, 94 },   { 4, "UG10", 83, 95 },
661 	{ 4, "UG12", 84, 96 },   { 4, "UG13", 85, 97 },  { 4, "UG14", 86, 98 },   { 4, "UG15", 87, 99 },
662 	{ 4, "UG16", 16, 100 },  { 4, "UG17", 88, 101 }, { 4, "UG18", 89, 102 },  { 4, "UG19", 90, 103 },
663 
664 	{ 0, NULL, 0, 0 }
665 };
666 
cmdScene(int argc,const char ** argv)667 bool Debugger::cmdScene(int argc, const char **argv) {
668 	if (argc != 0 && argc > 4) {
669 		debugPrintf("Changes set and scene.\n");
670 		debugPrintf("Usage: %s [(<chapterId> <setId> <sceneId>) | (<chapterId> <sceneName>) | <sceneName>]\n", argv[0]);
671 		return true;
672 	}
673 
674 	// scene <chapterId> <setId> <sceneId>
675 	if (argc == 4 && Common::isDigit(*argv[1]) && Common::isDigit(*argv[2]) && Common::isDigit(*argv[3])) {
676 		int chapterId = atoi(argv[1]);
677 		int setId = atoi(argv[2]);
678 		int sceneId = atoi(argv[3]);
679 
680 		if (chapterId < 1 || chapterId > 5) {
681 			debugPrintf("chapterID must be between 1 and 5\n");
682 			return true;
683 		}
684 
685 		int chapterIdNormalized = chapterId;
686 
687 		if (chapterId == 3 || chapterId == 5) {
688 			chapterIdNormalized = chapterId - 1;
689 		}
690 
691 		// Sanity check
692 		uint i;
693 		for (i = 0; sceneList[i].chapter != 0; i++) {
694 			if (sceneList[i].chapter == chapterIdNormalized &&
695 			    sceneList[i].set == setId &&
696 			    sceneList[i].scene == sceneId
697 			) {
698 				break;
699 			}
700 		}
701 
702 		if (sceneList[i].chapter == 0) { // end of list
703 			debugPrintf("chapterId, setId and sceneId combination is not valid.\n");
704 			return true;
705 		}
706 
707 		if (chapterId != _vm->_settings->getChapter()) {
708 			_vm->_settings->setChapter(chapterId);
709 		}
710 		_vm->_settings->setNewSetAndScene(setId, sceneId);
711 		return false;
712 	} else if (argc > 1) {
713 		int chapterId = 0;
714 		Common::String sceneName;
715 
716 		// <chapterId> <sceneName>
717 		if (argc == 3) {
718 			chapterId = atoi(argv[1]);
719 
720 			if (chapterId < 1 || chapterId > 5) {
721 				debugPrintf("chapterId must be between 1 and 5\n");
722 				return true;
723 			}
724 
725 			sceneName = argv[2];
726 		} else if (argc == 2) { // <sceneName>
727 			chapterId = _vm->_settings->getChapter();
728 			sceneName = argv[1];
729 		}
730 
731 		int chapterIdNormalized = chapterId;
732 
733 		if (chapterId == 3 || chapterId == 5) {
734 			chapterIdNormalized = chapterId - 1;
735 		}
736 
737 		uint i;
738 		for (i = 0; sceneList[i].chapter != 0; i++) {
739 			if (sceneList[i].chapter == chapterIdNormalized && sceneName.equalsIgnoreCase(sceneList[i].name))
740 				break;
741 		}
742 
743 		if (sceneList[i].chapter == 0) {
744 			debugPrintf("Invalid scene name or chapter.\n");
745 			return true;
746 		} else {
747 			if (chapterId != _vm->_settings->getChapter())
748 				_vm->_settings->setChapter(chapterId);
749 		}
750 
751 		_vm->_settings->setNewSetAndScene(sceneList[i].set, sceneList[i].scene);
752 		return false;
753 	}
754 
755 	int chapterId = _vm->_settings->getChapter();
756 	int chapterIdNormalized = chapterId;
757 
758 	if (chapterId == 3 || chapterId == 5) {
759 		chapterIdNormalized = chapterId - 1;
760 	}
761 
762 	uint i;
763 	for (i = 0; sceneList[i].chapter != 0; i++) {
764 		if (sceneList[i].chapter == chapterIdNormalized && sceneList[i].set == _vm->_scene->getSetId()
765 				&& sceneList[i].scene == _vm->_scene->getSceneId())
766 			break;
767 	}
768 
769 	debugPrintf("chapterID = %i\nsetId = %i\nsceneId = %i\nsceneName = '%s'\n", _vm->_settings->getChapter(), _vm->_scene->getSetId(),
770 				_vm->_scene->getSceneId(), sceneList[i].name);
771 	return true;
772 }
773 
cmdVariable(int argc,const char ** argv)774 bool Debugger::cmdVariable(int argc, const char **argv) {
775 	if (argc != 2 && argc != 3) {
776 		debugPrintf("Get or set game variable (integer).\n");
777 		debugPrintf("Usage: %s <id> [<value>]\n", argv[0]);
778 		return true;
779 	}
780 
781 	int variable = atoi(argv[1]);
782 	int variableCount = _vm->_gameInfo->getGlobalVarCount();
783 	if (variable >= 0 && variable < variableCount) {
784 		if (argc == 3) {
785 			_vm->_gameVars[variable] = atoi(argv[2]);
786 		}
787 		debugPrintf("variable(%i) = %i\n", variable, _vm->_gameVars[variable]);
788 	} else {
789 		debugPrintf("Variable id must be between 0 and %i\n", variableCount - 1);
790 	}
791 	return true;
792 }
793 
cmdClue(int argc,const char ** argv)794 bool Debugger::cmdClue(int argc, const char **argv) {
795 	if (argc != 3 && argc != 4) {
796 		debugPrintf("Get or changes clue for an actor.\n");
797 		debugPrintf("Usage: %s <actorId> <clueId> [<value>]\n", argv[0]);
798 		return true;
799 	}
800 
801 	int actorId = atoi(argv[1]);
802 
803 	Actor *actor = nullptr;
804 	if ((actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) || (actorId == kActorVoiceOver)) {
805 		actor = _vm->_actors[actorId];
806 	}
807 
808 	if (actor == nullptr) {
809 		debugPrintf("Unknown actor %i\n", actorId);
810 		return true;
811 	}
812 
813 	int clueId = atoi(argv[2]);
814 
815 	// TODO: check clueId
816 
817 	if (argc == 4) {
818 		int value = atoi(argv[3]);
819 		if (value != 0) {
820 			actor->acquireClue(clueId, true, -1);
821 		} else {
822 			actor->loseClue(clueId);
823 		}
824 	}
825 	debugPrintf("actorClue(%i, %i) = %i\n", actorId, clueId, actor->hasClue(clueId));
826 
827 	return true;
828 }
829 
cmdTimer(int argc,const char ** argv)830 bool Debugger::cmdTimer(int argc, const char **argv) {
831 	if (argc != 2 && argc != 4) {
832 		debugPrintf("Get or changes timers for an actor.\n");
833 		debugPrintf("Usage: %s <actorId> [<timer> <value>]\n", argv[0]);
834 		return true;
835 	}
836 
837 	int actorId = atoi(argv[1]);
838 
839 	Actor *actor = nullptr;
840 	if ((actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) || (actorId == kActorVoiceOver)) {
841 		actor = _vm->_actors[actorId];
842 	}
843 
844 	if (actor == nullptr) {
845 		debugPrintf("Unknown actor %i\n", actorId);
846 		return true;
847 	}
848 
849 	if (argc == 4) {
850 		int timer = atoi(argv[2]);
851 		int value = atoi(argv[3]);
852 
853 		if (timer < 0 || timer > 6) {
854 			debugPrintf("Timer must be [0..6]");
855 			return true;
856 		}
857 
858 		if (value == 0) {
859 			actor->timerReset(timer);
860 		} else {
861 			actor->timerStart(timer, value);
862 		}
863 	}
864 
865 	for (int i = 0; i < 7; ++i) {
866 		debugPrintf("actorTimer(%i, %i) = %u ms\n", actorId, i, actor->timerLeft(i));
867 	}
868 
869 	return true;
870 }
871 
cmdFriend(int argc,const char ** argv)872 bool Debugger::cmdFriend(int argc, const char **argv) {
873 	if (argc != 3 && argc != 4) {
874 		debugPrintf("Get or changes friendliness for an actor towards another actor.\n");
875 		debugPrintf("Usage: %s <actorId> <otherActorId> [<value>]\n", argv[0]);
876 		return true;
877 	}
878 
879 	int actorId = atoi(argv[1]);
880 
881 	Actor *actor = nullptr;
882 	if (actorId >= 0 && actorId < (int)_vm->_gameInfo->getActorCount()) {
883 		actor = _vm->_actors[actorId];
884 	}
885 
886 	if (actor == nullptr) {
887 		debugPrintf("Unknown actor %i\n", actorId);
888 		return true;
889 	}
890 
891 	int otherActorId = atoi(argv[2]);
892 
893 	if (otherActorId < 0 && otherActorId >= (int)_vm->_gameInfo->getActorCount()) {
894 		debugPrintf("Unknown actor %i\n", otherActorId);
895 	}
896 
897 	if (argc == 4) {
898 		int value = atoi(argv[3]);
899 
900 		if (value < 0 || value > 100) {
901 			debugPrintf("Value must be [0..100]");
902 			return true;
903 		}
904 
905 		actor->setFriendlinessToOther(otherActorId, value);
906 	}
907 
908 	debugPrintf("actorFriendliness(%i, %i) = %i\n", actorId, otherActorId, actor->getFriendlinessToOther(otherActorId));
909 
910 	return true;
911 }
912 
cmdLoad(int argc,const char ** argv)913 bool Debugger::cmdLoad(int argc, const char **argv) {
914 	if (argc != 2) {
915 		debugPrintf("Loads a save game from original format.\n");
916 		debugPrintf("Usage: %s <file path>\n", argv[0]);
917 		return true;
918 	}
919 
920 	Common::FSNode fs(argv[1]);
921 
922 	if (!fs.isReadable()) {
923 		debugPrintf("Warning: File %s does not exist or is not readable\n", argv[1]);
924 		return true;
925 	}
926 
927 	Common::SeekableReadStream *saveFile = fs.createReadStream();
928 
929 	_vm->loadGame(*saveFile);
930 
931 	delete saveFile;
932 
933 	return false;
934 }
935 
cmdSave(int argc,const char ** argv)936 bool Debugger::cmdSave(int argc, const char **argv) {
937 	if (argc != 2) {
938 		debugPrintf("Saves game to original format.\n");
939 		debugPrintf("Usage: %s <file path>\n", argv[0]);
940 		return true;
941 	}
942 
943 	Common::FSNode fs(argv[1]);
944 
945 	if (fs.exists() && !fs.isWritable()) {
946 		debugPrintf("Warning: File %s is not writable\n", argv[1]);
947 		return true;
948 	}
949 
950 	Common::WriteStream *saveFile = fs.createWriteStream();
951 
952 	Graphics::Surface thumbnail = _vm->generateThumbnail();
953 
954 	_vm->_time->pause();
955 	_vm->saveGame(*saveFile, thumbnail);
956 	_vm->_time->resume();
957 
958 	saveFile->finalize();
959 
960 	thumbnail.free();
961 
962 	delete saveFile;
963 
964 	return false;
965 }
966 
967 const struct OverlayAndScenesVQAsList {
968 	int resourceId;
969 	const char *name;
970 	bool isOverlayVQA; // else it is a scene VQA
971 } overlaysList[] = {
972 	{ 1, "MA04OVR2", true }, { 1, "PS10", false },    { 1, "MA01", false },    { 1, "RC01", false },    { 1, "PS01", false },
973 	{ 1, "CT01", false },    { 1, "PS11", false },    { 1, "RC51", false },    { 1, "MA02", false },    { 1, "RC02", false },
974 	{ 1, "PS02", false },    { 1, "CT02", false },    { 1, "PS12", false },    { 1, "CT12", false },    { 1, "PS03", false },
975 	{ 1, "CT03", false },    { 1, "PS13", false },    { 1, "MA04", false },    { 1, "PS04", false },    { 1, "CT04", false },
976 	{ 1, "PS14", false },    { 1, "CT01SPNR", true }, { 1, "MA05", false },    { 1, "PS05", false },    { 1, "CT05", false },
977 	{ 1, "PS15", false },    { 1, "MA06", false },    { 1, "PS06", false },    { 1, "CT06", false },    { 1, "MA02OVER", true },
978 	{ 1, "CT02OVER", true }, { 1, "MA07", false },    { 1, "PS07", false },    { 1, "CT07", false },    { 1, "PS09", false },
979 	{ 1, "MA04OVER", true }, { 1, "PS05OVER", true }, { 1, "CT05OVER", true },
980 
981 	{ 2, "BB10OVR1", true }, { 2, "BB10OVR2", true }, { 2, "BB10OVR3", true }, { 2, "BB10OVR4", true }, { 2, "BB10OVR5", true },
982 	{ 2, "BB10_2", false },  { 2, "UG10_2", false },  { 2, "NR10_2", false },  { 2, "PS10_2", false },  { 2, "CT10_2", false },
983 	{ 2, "MA01_2", false },  { 2, "BB01_2", false },  { 2, "HC01_2", false },  { 2, "RC01_2", false },  { 2, "HF01_2", false },
984 	{ 2, "UG01_2", false },  { 2, "AR01_2", false },  { 2, "DR01_2", false },  { 2, "NR01_2", false },  { 2, "PS01_2", false },
985 	{ 2, "CT01_2", false },  { 2, "BB11_2", false },  { 2, "NR11_2", false },  { 2, "PS11_2", false },  { 2, "CT11_2", false },
986 	{ 2, "BB51_2", false },  { 2, "CT51_2", false },  { 2, "MA02_2", false },  { 2, "BB02_2", false },  { 2, "TB02_2", false },
987 	{ 2, "HC02_2", false },  { 2, "HF02_2", false },  { 2, "UG02_2", false },  { 2, "AR02_2", false },  { 2, "DR02_2", false },
988 	{ 2, "NR02_2", false },  { 2, "PS02_2", false },  { 2, "CT02_2", false },  { 2, "BB12_2", false },  { 2, "PS12_2", false },
989 	{ 2, "CT12_2", false },  { 2, "MA04OVR2", true }, { 2, "BB03_2", false },  { 2, "HC03_2", false },  { 2, "RC03_2", false },
990 	{ 2, "HF03_2", false },  { 2, "UG03_2", false },  { 2, "DR03_2", false },  { 2, "NR03_2", false },  { 2, "PS03_2", false },
991 	{ 2, "CT03_2", false },  { 2, "PS13_2", false },  { 2, "MA04_2", false },  { 2, "BB04_2", false },  { 2, "HC04_2", false },
992 	{ 2, "RC04_2", false },  { 2, "HF04_2", false },  { 2, "UG04_2", false },  { 2, "DR04_2", false },  { 2, "NR04_2", false },
993 	{ 2, "PS04_2", false },  { 2, "CT04_2", false },  { 2, "PS14_2", false },  { 2, "DR06OVR2", true }, { 2, "MA05_2", false },
994 	{ 2, "BB05_2", false },  { 2, "TB05_2", false },  { 2, "HF05_2", false },  { 2, "DR05_2", false },  { 2, "NR05_2", false },
995 	{ 2, "PS05_2", false },  { 2, "CT05_2", false },  { 2, "PS15_2", false },  { 2, "MA06_2", false },  { 2, "BB06_2", false },
996 	{ 2, "TB06_2", false },  { 2, "HF06_2", false },  { 2, "UG06_2", false },  { 2, "DR06_2", false },  { 2, "NR06_2", false },
997 	{ 2, "PS06_2", false },  { 2, "CT06_2", false },  { 2, "MA07_2", false },  { 2, "BB07_2", false },  { 2, "TB07_2", false },
998 	{ 2, "NR07_2", false },  { 2, "PS07_2", false },  { 2, "BB08_2", false },  { 2, "NR08_2", false },  { 2, "CT08_2", false },
999 	{ 2, "BB09_2", false },  { 2, "NR09_2", false },  { 2, "PS09_2", false },  { 2, "CT09_2", false },  { 2, "NR11OVER", true },
1000 	{ 2, "CT01SPNR", true }, { 2, "MA02OVER", true }, { 2, "CT02OVER", true }, { 2, "BB12OVER", true }, { 2, "MA04OVER", true },
1001 	{ 2, "DR04OVER", true }, { 2, "NR04OVER", true }, { 2, "BB05OVER", true }, { 2, "DR05OVER", true }, { 2, "PS05OVER", true },
1002 	{ 2, "CT05OVER", true }, { 2, "BB06OVER", true }, { 2, "DR06OVER", true }, { 2, "BB07OVER", true }, { 2, "BB08OVER", true },
1003 
1004 	{ 3, "UG10_3", false },  { 3, "NR10_3", false },  { 3, "CT10_3", false },  { 3, "BB01_3", false },  { 3, "HC01_3", false },
1005 	{ 3, "RC01_3", false },  { 3, "HF01_3", false },  { 3, "UG01_3", false },  { 3, "KP01_3", false },  { 3, "AR01_3", false },
1006 	{ 3, "DR01_3", false },  { 3, "NR01_3", false },  { 3, "CT01_3", false },  { 3, "NR11_3", false },  { 3, "CT11_3", false },
1007 	{ 3, "BB51_3", false },  { 3, "RC51_3", false },  { 3, "CT51_3", false },  { 3, "MA02_3", false },  { 3, "BB02_3", false },
1008 	{ 3, "TB02_3", false },  { 3, "HC02_3", false },  { 3, "RC02_3", false },  { 3, "HF02_3", false },  { 3, "UG02_3", false },
1009 	{ 3, "KP02_3", false },  { 3, "AR02_3", false },  { 3, "DR02_3", false },  { 3, "NR02_3", false },  { 3, "CT02_3", false },
1010 	{ 3, "UG12_3", false },  { 3, "CT12_3", false },  { 3, "MA04OVR2", true }, { 3, "BB03_3", false },  { 3, "TB03_3", false },
1011 	{ 3, "HC03_3", false },  { 3, "RC03_3", false },  { 3, "HF03_3", false },  { 3, "UG03_3", false },  { 3, "KP03_3", false },
1012 	{ 3, "DR03_3", false },  { 3, "NR03_3", false },  { 3, "CT03_3", false },  { 3, "UG13_3", false },  { 3, "MA04_3", false },
1013 	{ 3, "BB04_3", false },  { 3, "HC04_3", false },  { 3, "RC04_3", false },  { 3, "HF04_3", false },  { 3, "UG04_3", false },
1014 	{ 3, "KP04_3", false },  { 3, "DR04_3", false },  { 3, "NR04_3", false },  { 3, "CT04_3", false },  { 3, "UG14_3", false },
1015 	{ 3, "PS14_3", false },  { 3, "DR06OVR2", true }, { 3, "MA05_3", false },  { 3, "HF05_3", false },  { 3, "UG05_3", false },
1016 	{ 3, "KP05_3", false },  { 3, "DR05_3", false },  { 3, "NR05_3", false },  { 3, "CT05_3", false },  { 3, "UG15_3", false },
1017 	{ 3, "MA06_3", false },  { 3, "HF06_3", false },  { 3, "UG06_3", false },  { 3, "KP06_3", false },  { 3, "DR06_3", false },
1018 	{ 3, "NR06_3", false },  { 3, "CT06_3", false },  { 3, "UG16_3", false },  { 3, "UG18OVR2", true }, { 3, "UG19OVR1", true },
1019 	{ 3, "MA07_3", false },  { 3, "TB07_3", false },  { 3, "HF07_3", false },  { 3, "UG07_3", false },  { 3, "KP07_3", false },
1020 	{ 3, "NR07_3", false },  { 3, "UG17_3", false },  { 3, "UG08_3", false },  { 3, "NR08_3", false },  { 3, "CT08_3", false },
1021 	{ 3, "UG18_3", false },  { 3, "UG09_3", false },  { 3, "NR09_3", false },  { 3, "PS09_3", false },  { 3, "CT09_3", false },
1022 	{ 3, "UG19_3", false },  { 3, "NR11OVER", true }, { 3, "CT01SPNR", true }, { 3, "MA02OVER", true }, { 3, "CT02OVER", true },
1023 	{ 3, "MA04OVER", true }, { 3, "DR04OVER", true }, { 3, "NR04OVER", true }, { 3, "UG14OVER", true }, { 3, "DR05OVER", true },
1024 	{ 3, "CT05OVER", true }, { 3, "UG15OVER", true }, { 3, "DR06OVER", true }, { 3, "UG17OVER", true }, { 3, "UG18OVER", true },
1025 
1026 	{ 6, "VKLUCY", true },   { 6, "VKRUNC", true },   { 6, "KIA_CLUE", false },{ 6, "KIA_INGM", false }, { 6, "KIA_CRIM", false },
1027 	{ 6, "KIA_SUSP", false },{ 6, "HC01ESP1", false },{ 6, "HC01ESP2", false },{ 6, "HC01ESP3", false }, { 6, "RC02ESP1", false },
1028 	{ 6, "HC02ESP2", false },{ 6, "RC02ESP2", false },{ 6, "HC02ESP3", false },{ 6, "RC02ESP3", false }, { 6, "HC02ESP4", false },
1029 	{ 6, "RC02ESP4", false },{ 6, "HC02ESP5", false },{ 6, "RC02ESP5", false },{ 6, "RC02ESP6", false }, { 6, "RC02ESP7", false },
1030 	{ 6, "TB06ESP1", false },{ 6, "KP06ESP1", false },{ 6, "NR06ESP1", false },{ 6, "TB06ESP2", false }, { 6, "KP06ESP2", false },
1031 	{ 6, "NR06ESP2", false },{ 6, "TB06ESP3", false },{ 6, "KP06ESP3", false },{ 6, "NR07ESP1", false }, { 6, "TB06ESP4", false },
1032 	{ 6, "KP06ESP4", false },{ 6, "NR07ESP2", false },{ 6, "SPINNER", false }, { 6, "KIAOVER", false },  { 6, "VK", false },
1033 	{ 6, "VKKASH", true },   { 6, "PS02ELEV", false },{ 6, "ESPER", false },   { 6, "VKDEKT", true },   { 6, "MA06ELEV", false },
1034 	{ 6, "VKBOB", true },    { 6, "SCORE", false },
1035 
1036 	{ 0, NULL, false }
1037 };
1038 
1039 /**
1040 * Will use overlay videos that the game has loaded for the scene
1041 * at the time of running the command
1042 * or otherwise will attempt to load the specified overlay to the scene,
1043 * if it exists in the currently loaded (VQAx, MODE) MIX resources.
1044 * Use "overlay reset" to clear up all loaded overlays (and/or custom scene video)
1045 *
1046 * Note: Loading MODE.MIX here (and a VQA from it) may lead to buggy results,
1047 *       if the player then invokes and closes an actual game mode (KIA, ESPER, GPS etc).
1048 *       This is because the game itself will unload MODE.MIX when closing the game mode
1049 *       and that can lead to a assertion fault for a missing file handle.
1050 *       A viable solution would be to have MODE.MIX loaded all the time,
1051 *       but that is unnecessary since a developer could just uncomment a few lines below
1052 *       (look for "force-load MODE.MIX") and make use of it with caution, if needed.
1053 */
cmdOverlay(int argc,const char ** argv)1054 bool Debugger::cmdOverlay(int argc, const char **argv) {
1055 	bool invalidSyntax = false;
1056 
1057 	if (_vm->_kia->isOpen()
1058 		|| _vm->_esper->isOpen()
1059 		|| _vm->_spinner->isOpen()
1060 		|| _vm->_elevator->isOpen()
1061 		|| _vm->_vk->isOpen()
1062 		|| _vm->_scores->isOpen()
1063 	) {
1064 		debugPrintf("Sorry, playing custom overlays in KIA, ESPER, Voigt-Kampff, Spinner GPS,\nScores or Elevator mode is not supported\n");
1065 		return true;
1066 	}
1067 
1068 	if (argc != 1 && argc != 2 && argc != 3 && argc != 5) {
1069 		invalidSyntax = true;
1070 	} else {
1071 		bool modeMixOverlaysAvailableFlg = false;
1072 		int chapterIdOverlaysAvailableInt = -1;
1073 
1074 		if (_vm->_chapters->hasOpenResources()) {
1075 			chapterIdOverlaysAvailableInt = MIN(_vm->_chapters->currentResourceId(), 3);
1076 		}
1077 		if (chapterIdOverlaysAvailableInt == -1) {
1078 			debugPrintf("No available open resources to load VQAs from.\n Giving up.\n");
1079 			return true;
1080 		}
1081 
1082 		// Normally, don't force-load the MODE.MIX resource
1083 		if (!_vm->isArchiveOpen("MODE.MIX")) {
1084 	//		if (_vm->openArchive("MODE.MIX") { // Note: This will force-load MODE.MIX. Use with caution!
1085 	//			debugPrintf("Warning: MODE.MIX resources were force-loaded.\n Please, don't use game's menu modes (KIA, ESPER, Voigt-Kampff, Spinner GPS, Scores or Elevator) before executing an \"%s reset\" from the debugger!\n", argv[0]);
1086 	//			modeMixOverlaysAvailableFlg = true;
1087 	//		}
1088 		} else {
1089 			modeMixOverlaysAvailableFlg = true;
1090 		}
1091 
1092 		if (argc == 1) {
1093 			// print info for all overlays loaded for the scene
1094 			uint8 countOfLoadedOverlaysInScene = 0;
1095 			debugPrintf("name animationId startFrame endFrame\n");
1096 
1097 			for (int i = 0; i < _vm->_overlays->kOverlayVideos; ++i) {
1098 				if (_vm->_overlays->_videos[i].loaded) {
1099 					countOfLoadedOverlaysInScene++;
1100 					VQADecoder::LoopInfo &loopInfo =_vm->_overlays->_videos[i].vqaPlayer->_decoder._loopInfo;
1101 					for (int j = 0; j < loopInfo.loopCount; ++j) {
1102 						debugPrintf("%s %2d %4d %4d\n", _vm->_overlays->_videos[i].name.c_str(), j, loopInfo.loops[j].begin, loopInfo.loops[j].end);
1103 					}
1104 				}
1105 			}
1106 
1107 			if ( countOfLoadedOverlaysInScene > 0) {
1108 				debugPrintf("  ** %d overlays are loaded in scene **\n", countOfLoadedOverlaysInScene);
1109 			} else {
1110 				debugPrintf("  ** No overlays loaded in scene **\n");
1111 			}
1112 
1113 			return true;
1114 		}
1115 
1116 		if (argc == 2) {
1117 			Common::String argName = argv[1];
1118 
1119 			if (argName == "reset") {
1120 			// Reset (remove) the overlays loaded for the scene
1121 				_vm->_overlays->removeAll();
1122 				// And return to original VQA for this scene
1123 				const Common::String origSceneName = _vm->_gameInfo->getSceneName(_vm->_scene->_sceneId);
1124 
1125 				Common::String origVqaName;
1126 				if (chapterIdOverlaysAvailableInt == 1) {
1127 					origVqaName = Common::String::format("%s.VQA", origSceneName.c_str());
1128 				} else {
1129 					origVqaName = Common::String::format("%s_%d.VQA", origSceneName.c_str(), chapterIdOverlaysAvailableInt);
1130 				}
1131 
1132 				if (_vm->_scene->_vqaPlayer != nullptr) {
1133 					delete _vm->_scene->_vqaPlayer;
1134 				}
1135 
1136 				_vm->_scene->_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, origVqaName);
1137 				if (!_vm->_scene->_vqaPlayer->open()) {
1138 					debugPrintf("Error: Could not open player while reseting\nto scene VQA named: %s!\n", (origVqaName + ".VQA").c_str());
1139 					return true;
1140 				}
1141 				_vm->_scene->startDefaultLoop();
1142 				_vm->_scene->advanceFrame();
1143 
1144 
1145 			} else if (argName == "avail") {
1146 			// List the available overlays in the loaded resources
1147 				const uint dispColCount = 5;
1148 				uint colCountIter = 0;
1149 				uint16 itemIter = 0;
1150 
1151 				debugPrintf("Available overlays in the loaded resources:\n");
1152 				for (itemIter = 0; overlaysList[itemIter].resourceId != 0; ++itemIter) {
1153 					if ( (overlaysList[itemIter].resourceId == chapterIdOverlaysAvailableInt)
1154 						|| ( modeMixOverlaysAvailableFlg && overlaysList[itemIter].resourceId == 6)
1155 					) {
1156 						debugPrintf("%s ", overlaysList[itemIter].name);
1157 						colCountIter = (colCountIter + 1) % dispColCount;
1158 						if ( colCountIter == 0) {
1159 							debugPrintf("\n");
1160 						}
1161 					}
1162 				}
1163 				// final new line if needed
1164 				if ( colCountIter % dispColCount != 0) {
1165 					debugPrintf("\n");
1166 				}
1167 				if (!modeMixOverlaysAvailableFlg) {
1168 					debugPrintf("Note: MODE.MIX resources are currently not loaded.\n");
1169 				}
1170 
1171 			} else if (argName.size() > 12) {
1172 				debugPrintf("The specified name is too long. It should be up to 12 characters.\n");
1173 				invalidSyntax = true;
1174 			} else {
1175 				debugPrintf("Invalid command usage\n");
1176 				invalidSyntax = true;
1177 			}
1178 		}
1179 
1180 		if (!invalidSyntax && (argc == 3 || argc == 5)) {
1181 			Common::String overlayName = argv[1];
1182 			overlayName.toUppercase();
1183 
1184 			int overlayAnimationId = atoi(argv[2]);
1185 			bool loopForever = false;
1186 			LoopSetModes startNowFlag = kLoopSetModeEnqueue;
1187 
1188 			if (argc == 5 && atoi(argv[3]) != 0) {
1189 				loopForever = true;
1190 			}
1191 
1192 			if (argc == 5 && atoi(argv[4]) != 0) {
1193 				startNowFlag = kLoopSetModeImmediate;
1194 			}
1195 
1196 			if (overlayAnimationId < 0) {
1197 				debugPrintf("Animation id value must be >= 0!\n");
1198 				return true;
1199 			}
1200 
1201 			// Check if specified overlay name exists AND is available
1202 			uint16 itemIter = 0;
1203 			for (itemIter = 0; overlaysList[itemIter].resourceId != 0; ++itemIter) {
1204 				if ( (overlaysList[itemIter].resourceId == chapterIdOverlaysAvailableInt)
1205 					|| ( modeMixOverlaysAvailableFlg && overlaysList[itemIter].resourceId == 6)
1206 				) {
1207 					if (strcmp(overlaysList[itemIter].name, overlayName.c_str()) == 0) {
1208 						break;
1209 					}
1210 				}
1211 			}
1212 			if (overlaysList[itemIter].resourceId == 0 ) {
1213 				debugPrintf("No available resource was found by that name!\nPerhaps it exists in another chapter.\n");
1214 				return true;
1215 			}
1216 
1217 			if (overlaysList[itemIter].isOverlayVQA) {
1218 				//
1219 				// Attempt to load the overlay in an empty slot
1220 				// even if it's not already loaded for the scene (in _vm->_overlays->_videos)
1221 				int overlayVideoIdx = _vm->_overlays->play(overlayName, overlayAnimationId, loopForever, startNowFlag, 0);
1222 				if ( overlayVideoIdx == -1 ) {
1223 					debugPrintf("Could not load the overlay animation: %s in this scene. Try reseting overlays first to free up slots!\n", overlayName.c_str());
1224 				} else {
1225 					debugPrintf("Loading overlay animation: %s...\n", overlayName.c_str());
1226 
1227 					VQADecoder::LoopInfo &loopInfo =_vm->_overlays->_videos[overlayVideoIdx].vqaPlayer->_decoder._loopInfo;
1228 					int overlayAnimationLoopCount = loopInfo.loopCount;
1229 					if (overlayAnimationLoopCount == 0) {
1230 						debugPrintf("Error: No valid loops were found for overlay animation named: %s!\n", overlayName.c_str());
1231 						_vm->_overlays->remove(overlayName.c_str());
1232 					} else if (overlayAnimationId >= overlayAnimationLoopCount) {
1233 						debugPrintf("Invalid loop id: %d for overlay animation: %s. Try from 0 to %d.\n",  overlayAnimationId, overlayName.c_str(), overlayAnimationLoopCount-1);
1234 					} else {
1235 						// print info about available loops too
1236 						debugPrintf("Animation: %s loaded. Running loop %d...\n", overlayName.c_str(), overlayAnimationId);
1237 						for (int j = 0; j < overlayAnimationLoopCount; ++j) {
1238 							debugPrintf("%s %2d %4d %4d\n", _vm->_overlays->_videos[overlayVideoIdx].name.c_str(), j, loopInfo.loops[j].begin, loopInfo.loops[j].end);
1239 						}
1240 					}
1241 				}
1242 			} else {
1243 				if (_vm->_scene->_vqaPlayer != nullptr) {
1244 					delete _vm->_scene->_vqaPlayer;
1245 				}
1246 				_vm->_scene->_vqaPlayer = new VQAPlayer(_vm, &_vm->_surfaceBack, overlayName + ".VQA");
1247 				if (!_vm->_scene->_vqaPlayer->open()) {
1248 					debugPrintf("Error: Could not open player for scene VQA named: %s!\n", (overlayName + ".VQA").c_str());
1249 					return true;
1250 				}
1251 
1252 				VQADecoder::LoopInfo &loopInfo =_vm->_scene->_vqaPlayer->_decoder._loopInfo;
1253 				int sceneAnimationLoopCount = loopInfo.loopCount;
1254 				if (sceneAnimationLoopCount == 0) {
1255 					debugPrintf("Error: No valid loops were found for scene animation named: %s!\n", (overlayName + ".VQA").c_str());
1256 				} else if (overlayAnimationId >= sceneAnimationLoopCount) {
1257 					debugPrintf("Invalid loop id: %d for scene animation: %s. Try from 0 to %d.\n",  overlayAnimationId, overlayName.c_str(), sceneAnimationLoopCount-1);
1258 				} else {
1259 					// ignore the specified loopForever and startNow flags
1260 					// just do a kSceneLoopModeOnce, without immediate start
1261 					_vm->_scene->loopStartSpecial(kSceneLoopModeOnce, overlayAnimationId, false);
1262 					debugPrintf("Scene animation: %s loaded. Running loop %d...\n", overlayName.c_str(), overlayAnimationId);
1263 					for (int j = 0; j < sceneAnimationLoopCount; ++j) {
1264 						debugPrintf("%s %2d %4d %4d\n", overlayName.c_str(), j, loopInfo.loops[j].begin, loopInfo.loops[j].end);
1265 					}
1266 				}
1267 			}
1268 		}
1269 	}
1270 
1271 	if (invalidSyntax) {
1272 		debugPrintf("Load, list, play or reset (clear) loaded overlay animations.\nValues for loopForever and startNow are boolean.\n");
1273 		debugPrintf("Usage: %s [[<name> <animationId> [<loopForever> <startNow>]] | avail | reset ]\n", argv[0]);
1274 	}
1275 	return true;
1276 }
1277 
1278 /**
1279 *
1280 * Show an explicitly specified string as a subtitle
1281 */
cmdSubtitle(int argc,const char ** argv)1282 bool Debugger::cmdSubtitle(int argc, const char **argv) {
1283 	bool invalidSyntax = false;
1284 
1285 	if (argc != 2) {
1286 		invalidSyntax = true;
1287 	} else {
1288 		if (!_vm->_subtitles->isSystemActive()) {
1289 			debugPrintf("Subtitles system is currently disabled\n");
1290 		}
1291 
1292 		Common::String subtitleText = argv[1];
1293 		if (subtitleText == "info") {
1294 			debugPrintf("Subtitles version info: v%s (%s) %s by: %s\n",
1295 			            _vm->_subtitles->getSubtitlesInfo().versionStr.c_str(),
1296 			            _vm->_subtitles->getSubtitlesInfo().dateOfCompile.c_str(),
1297 			            _vm->_subtitles->getSubtitlesInfo().languageMode.c_str(),
1298 			            _vm->_subtitles->getSubtitlesInfo().credits.c_str());
1299 			debugPrintf("Subtitles font loaded: %s\n",
1300 			            _vm->_subtitles->getSubtitlesInfo().fontName.c_str());
1301 
1302 		} else if (subtitleText == "reset") {
1303 			_vm->_subtitles->setGameSubsText("", false);
1304 		} else {
1305 			debugPrintf("Showing text: %s\n", subtitleText.c_str());
1306 			_vm->_subtitles->setGameSubsText(subtitleText, true);
1307 			_vm->_subtitles->show();
1308 		}
1309 	}
1310 
1311 	if (invalidSyntax) {
1312 		debugPrintf("Show subtitles info, or display and clear (reset) a specified text as subtitle or clear the current subtitle.\n");
1313 		debugPrintf("Use double quotes to encapsulate the text.\n");
1314 		debugPrintf("Usage: %s (\"<text_to_display>\" | info | reset)\n", argv[0]);
1315 	}
1316 	return true;
1317 
1318 }
1319 
1320 /**
1321 * Toggle showing Maze score on the fly as subtitle during the Police Maze Course
1322 */
cmdMazeScore(int argc,const char ** argv)1323 bool Debugger::cmdMazeScore(int argc, const char **argv) {
1324 	bool invalidSyntax = false;
1325 
1326 	if (argc != 2) {
1327 		invalidSyntax = true;
1328 	} else {
1329 		if (_vm->_scene->getSetId() != kSetPS10_PS11_PS12_PS13) {
1330 			debugPrintf("Error:Command %s is only valid during the Police Maze course\n",  argv[0]);
1331 			return true;
1332 		}
1333 		//
1334 		// set a debug variable to enable showing the maze score
1335 		//
1336 		Common::String argName = argv[1];
1337 		argName.toLowercase();
1338 		if (argc == 2 && argName == "toggle") {
1339 			_showMazeScore = !_showMazeScore;
1340 			debugPrintf("Showing maze score = %s\n", _showMazeScore ? "True":"False");
1341 		} else {
1342 			invalidSyntax = true;
1343 		}
1344 	}
1345 
1346 	if (invalidSyntax) {
1347 		debugPrintf("Toggle showing the Maze Score as a subtitle during the Shooting Grounds Course\n");
1348 		debugPrintf("Usage: %s toggle\n", argv[0]);
1349 	}
1350 	return true;
1351 }
1352 
1353 
1354 /**
1355 * Add, remove or edit flags or bounding box for an object to debug some issues whereby existing obstacle layout does not prevent
1356 * characters from walking where they shouldn't
1357 *
1358 * Notes: with regard to Bound box / the clickable area for an object, item or region
1359 * 1. The code for what a mouse click will refer to is in BladeRunnerEngine::handleMouseAction() -- under if (mainButton) clause
1360 *    Clickable 3d areas for boxes are determined based on scene information that translates the 2d mouse position into
1361 *    a "3d" position in the view. (see: Mouse::getXYZ)
1362 *    This 3d position must be within the bounding box of the 3d object
1363 *
1364 * 2. For items, the mouse-over (Item::isUnderMouse) depends on the screenRectangle dimensions (Common::Rect) and specifies the
1365 *    targetable area for the item. The screenRectangle is calculated at every tick() of the item and depends on the item's animation,
1366 *    current frame and thus facing angle.
1367 *    The item's bounding box still determines the clickable area for the item. It is calculated at item's setup() or setXYZ()
1368 *    and depends on the item's position (vector), height and width.
1369 */
cmdObject(int argc,const char ** argv)1370 bool Debugger::cmdObject(int argc, const char **argv) {
1371 	bool invalidSyntax = false;
1372 
1373 	if (argc < 3) {
1374 		invalidSyntax = true;
1375 	} else {
1376 		Common::String modeName = argv[1];
1377 		modeName.toLowercase();
1378 		if (modeName == "add" && argc == 9) {
1379 			// add object mode
1380 			// first add to set and then to scene
1381 			Common::String custObjName = "CUSTOMOBJ";
1382 			Common::String custObjNameSuffix = argv[2];
1383 			custObjName = custObjName + custObjNameSuffix;
1384 			//
1385 			// plain 3d objects (non-items) are added to the Set by reading the specific data for that Set
1386 			// Their count is also stored in the Set's data,
1387 			// if there's a need for a permanent extra obstacle in the Set, the code in set.cpp will have to be overridden (hard coded patch)
1388 			//
1389 			// For the debugger's purposes it should be harmless to add custom objects to test blocking pathways or other functions
1390 			// they will persist in Save/Load but should go away when exiting a Set
1391 			if (_vm->_scene->_set->_objectCount > 85) { //85 is the size of the _objects array in set.cpp
1392 				debugPrintf("Unable to add more objects in the set\n");
1393 			} else {
1394 				int objectId = _vm->_scene->_set->_objectCount;
1395 				_vm->_scene->_set->_objectCount++;
1396 				_vm->_scene->_set->_objects[objectId].name = custObjName.c_str();
1397 
1398 				float x0, y0, z0, x1, y1, z1;
1399 				x0 = atof(argv[3]);
1400 				y0 = atof(argv[4]);
1401 				z0 = atof(argv[5]);
1402 				x1 = atof(argv[6]);
1403 				y1 = atof(argv[7]);
1404 				z1 = atof(argv[8]);
1405 
1406 				_vm->_scene->_set->_objects[objectId].bbox = BoundingBox(x0, y0, z0, x1, y1, z1);
1407 				_vm->_scene->_set->_objects[objectId].isObstacle = 0; // init as false
1408 				_vm->_scene->_set->_objects[objectId].isClickable = 0;// init as false
1409 				_vm->_scene->_set->_objects[objectId].isHotMouse = 0;
1410 				_vm->_scene->_set->_objects[objectId].unknown1 = 0;
1411 				_vm->_scene->_set->_objects[objectId].isTarget = 0;   // init as false
1412 				//
1413 				if (_vm->_sceneObjects->addObject(objectId + kSceneObjectOffsetObjects,
1414 				                                  _vm->_scene->_set->_objects[objectId].bbox,
1415 				                                  false,
1416 				                                  false,
1417 				                                  _vm->_scene->_set->_objects[objectId].unknown1,
1418 				                                  false)
1419 				) {
1420 					debugPrintf("Object: %d: %s was added to set and scene\n", objectId, custObjName.c_str());
1421 				} else {
1422 					debugPrintf("Failed to add object: %d: %s to the scene\n", objectId, custObjName.c_str());
1423 				}
1424 				return true;
1425 			}
1426 		} else if ((modeName == "remove" && argc == 3)
1427 		            || (modeName == "flags" && argc == 6)
1428 					|| (modeName == "bounds" && argc == 9)
1429 		            || (modeName == "list" && argc == 3)
1430 		) {
1431 			// remove object mode, show properties or edit flags
1432 			int objectId = atoi(argv[2]);
1433 			if (objectId >= 0 && objectId < _vm->_scene->_set->_objectCount) {
1434 				Common::String objName = _vm->_scene->objectGetName(objectId);
1435 				if (modeName == "list") {
1436 					// list properties
1437 					const BoundingBox &bbox = _vm->_scene->_set->_objects[objectId].bbox;
1438 					Vector3 a, b;
1439 					bbox.getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z);
1440 					Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b));
1441 					// Intentional? When loading a game, in the loaded Scene:
1442 					// an object can be an non-obstacle as a sceneObject but an obstacle as a Set object.
1443 					// and this seems to be considered as a non-obstacle by the game in that Scene.
1444 					// These are objects that the Unobstacle_Object() is called for when a scene is loaded (SceneLoaded())
1445 					// So the sceneObject property overrides the Set object property in these cases
1446 					// Eg. in PS01 BOX38 (object id: 19)
1447 					// This inconsistency is fixed if you exit and re-enter the scene.
1448 					debugPrintf("%d: %s (Clk: %s, Trg: %s, Obs: %s), Pos(%02.2f,%02.2f,%02.2f)\n     Bbox(%02.2f,%02.2f,%02.2f) ~ (%02.2f,%02.2f,%02.2f)\n",
1449 					            objectId, objName.c_str(),
1450 					            _vm->_scene->_set->_objects[objectId].isClickable? "T" : "F",
1451 					            _vm->_scene->_set->_objects[objectId].isTarget?    "T" : "F",
1452 					            _vm->_scene->_set->_objects[objectId].isObstacle?  "T" : "F",
1453 					            pos.x, pos.y, pos.z,
1454 					            a.x, a.y, a.z, b.x, b.y, b.z);
1455 				} else if (modeName == "remove") {
1456 					// scene's objectSetIsObstacle will update obstacle and walkpath
1457 					_vm->_scene->objectSetIsObstacle(objectId, false, !_vm->_sceneIsLoading, true);
1458 					// remove only from scene objects (keep in Set)
1459 					if (!_vm->_sceneIsLoading && _vm->_sceneObjects->remove(objectId + kSceneObjectOffsetObjects)) {
1460 						debugPrintf("Object: %d: %s was removed\n", objectId, objName.c_str());
1461 					} else {
1462 						debugPrintf("Failed to remove object: %d: %s\n", objectId, objName.c_str());
1463 					}
1464 				} else if (modeName == "bounds") {
1465 					Vector3 positionBottomRight(atof(argv[3]), atof(argv[4]), atof(argv[5]));
1466 					Vector3 positionTopLeft(atof(argv[6]), atof(argv[7]), atof(argv[8]));
1467 					_vm->_scene->_set->_objects[objectId].bbox.setXYZ(positionBottomRight.x, positionBottomRight.y, positionBottomRight.z, positionTopLeft.x, positionTopLeft.y, positionTopLeft.z);
1468 					if (!_vm->_sceneIsLoading && _vm->_sceneObjects->remove(objectId + kSceneObjectOffsetObjects)) {
1469 						_vm->_sceneObjects->addObject(objectId + kSceneObjectOffsetObjects,
1470 						                               _vm->_scene->_set->_objects[objectId].bbox,
1471 						                               _vm->_scene->_set->_objects[objectId].isClickable,
1472 						                               _vm->_scene->_set->_objects[objectId].isObstacle,
1473 						                               _vm->_scene->_set->_objects[objectId].unknown1,
1474 						                               _vm->_scene->_set->_objects[objectId].isTarget);
1475 						// scene's objectSetIsObstacle will update obstacle and walkpath
1476 						_vm->_scene->objectSetIsObstacle(objectId, _vm->_scene->_set->_objects[objectId].isObstacle, !_vm->_sceneIsLoading, true);
1477 						debugPrintf("New bounds: (%02.2f,%02.2f,%02.2f) ~ (%02.2f,%02.2f,%02.2f)\n",
1478 						             positionBottomRight.x, positionBottomRight.y, positionBottomRight.z,
1479 						             positionTopLeft.x,   positionTopLeft.y,   positionTopLeft.z);
1480 					}
1481 				} else {
1482 					// edit flags
1483 					bool newClickable = atoi(argv[3])? true : false;
1484 					bool newTarget    = atoi(argv[4])? true : false;
1485 					bool newObstacle  = atoi(argv[5])? true : false;
1486 					// scene's objectSetIsObstacle will update obstacle and walkpath
1487 					_vm->_scene->objectSetIsObstacle(objectId, newObstacle, !_vm->_sceneIsLoading, true);
1488 					_vm->_scene->objectSetIsClickable(objectId, newClickable, !_vm->_sceneIsLoading);
1489 					_vm->_scene->objectSetIsTarget(objectId, newTarget, !_vm->_sceneIsLoading);
1490 
1491 					debugPrintf("Setting obj %d: %s as clickable: %s, target: %s, obstacle: %s\n", objectId, objName.c_str(), newClickable? "T":"F", newTarget? "T":"F", newObstacle? "T":"F");
1492 				}
1493 				return true;
1494 			} else {
1495 				debugPrintf("Invalid object id %d was specified\n", objectId);
1496 				return true;
1497 			}
1498 		} else {
1499 			invalidSyntax = true;
1500 		}
1501 	}
1502 
1503 	if (invalidSyntax) {
1504 		debugPrintf("Add, edit flags, bounds or remove a 3d object obstacle in the current scene\n");
1505 		debugPrintf("Use debugger command List with \"obj\" argument to view available targets for this command\n");
1506 		debugPrintf("Floats:   brX, brY, brZ, tlX, tlY, tlZ, posX, posY, posZ\n");
1507 		debugPrintf("Integers: id, incId\n");
1508 		debugPrintf("Boolean (1: true, 0: false): isObstacle, isClickable, isTarget\n");
1509 		debugPrintf("Usage 1: %s add    <incId> <brX> <brY> <brZ>  <tlX> <tlY> <tlZ>\n", argv[0]);
1510 		debugPrintf("Usage 2: %s list   <id>\n", argv[0]);
1511 		debugPrintf("Usage 3: %s flags  <id> <isClickable> <isTarget> <isObstacle>\n", argv[0]);
1512 		debugPrintf("Usage 4: %s bounds <id> <brX> <brY> <brZ>  <tlX> <tlY> <tlZ>\n", argv[0]);
1513 		debugPrintf("Usage 5: %s remove <id>\n", argv[0]);
1514 	}
1515 	return true;
1516 }
1517 
1518 /**
1519 * Add a new, remove or edit flags and bounds for an existing item
1520 */
cmdItem(int argc,const char ** argv)1521 bool Debugger::cmdItem(int argc, const char **argv) {
1522 	bool invalidSyntax = false;
1523 
1524 	if (argc < 3) {
1525 		invalidSyntax = true;
1526 	} else {
1527 		Common::String modeName = argv[1];
1528 		modeName.toLowercase();
1529 		int itemId = atoi(argv[2]);
1530 		if (itemId < 0) {
1531 			debugPrintf("Invalid item id: %d specified. Item id must be an integer >=0\n", itemId);
1532 			return true;
1533 		}
1534 
1535 		if (modeName == "add" && argc == 10) {
1536 			// add item mode
1537 			if (_vm->_sceneObjects->findById(itemId + kSceneObjectOffsetItems) == -1) {
1538 				Vector3 itemPosition(atof(argv[3]), atof(argv[4]), atof(argv[5]));
1539 				int itemFacing = atoi(argv[6]);
1540 				int itemHeight = atoi(argv[7]);
1541 				int itemWidth = atoi(argv[8]);
1542 				int itemAnimationId = atoi(argv[9]);
1543 				if (_vm->_items->addToWorld(itemId, itemAnimationId, _vm->_scene->_setId, itemPosition, itemFacing, itemHeight, itemWidth, false, true, false, true)) {
1544 					debugPrintf("Item: %d was added to set and scene\n", itemId);
1545 				} else {
1546 					debugPrintf("Failed to add item: %d to the scene\n", itemId);
1547 				}
1548 				return true;
1549 			} else {
1550 				debugPrintf("Item: %d is already in the scene\n", itemId);
1551 			}
1552 		} else if ((modeName == "remove" && argc == 3)
1553 		            || (modeName == "flags" && argc == 5)
1554 		            || (modeName == "bounds" && argc == 9)
1555 		            || (modeName == "list" && argc == 3)
1556 		) {
1557 			// remove item mode, show properties or edit flags
1558 			if (_vm->_sceneObjects->findById(itemId + kSceneObjectOffsetItems) != -1) {
1559 				if (modeName == "list") {
1560 					// list properties
1561 					float xpos_curr, ypos_curr, zpos_curr, x0_curr, y0_curr, z0_curr, x1_curr, y1_curr, z1_curr;
1562 					int currHeight, currWidth;
1563 					int itemAnimationId;
1564 					int facing_curr = _vm->_items->getFacing(itemId);
1565 					_vm->_items->getWidthHeight(itemId, &currWidth, &currHeight);
1566 					_vm->_items->getXYZ(itemId, &xpos_curr, &ypos_curr, &zpos_curr );
1567 					_vm->_items->getBoundingBox(itemId).getXYZ(&x0_curr, &y0_curr, &z0_curr, &x1_curr, &y1_curr, &z1_curr);
1568 					_vm->_items->getAnimationId(itemId, &itemAnimationId);
1569 					const Common::Rect &screenRect = _vm->_items->getScreenRectangle(itemId);
1570 					debugPrintf("Item %d (Trg: %s, Vis/Clk: %s) Pos(%02.2f,%02.2f,%02.2f), Face: %d, Height: %d, Width: %d AnimId: %d\n ScrRct(%d,%d,%d,%d) Bbox(%02.2f,%02.2f,%02.2f) ~ (%02.2f,%02.2f,%02.2f)\n",
1571 					            itemId,
1572 					            _vm->_items->isTarget(itemId)?  "T" : "F",
1573 					            _vm->_items->isVisible(itemId)? "T" : "F",
1574 					            xpos_curr, ypos_curr, zpos_curr,
1575 					            facing_curr, currWidth, currHeight, itemAnimationId,
1576 					            screenRect.top, screenRect.left, screenRect.bottom, screenRect.right,
1577 					            x0_curr, y0_curr, z0_curr, x1_curr, y1_curr, z1_curr);
1578 				} else if (modeName == "remove") {
1579 					if (_vm->_sceneObjects->remove(itemId + kSceneObjectOffsetItems)) {
1580 						debugPrintf("Item: %d was removed\n", itemId);
1581 					} else {
1582 						debugPrintf("Failed to remove item: %d\n", itemId);
1583 					}
1584 				} else if (modeName == "bounds") {
1585 					// change position and facing to affect the screen rectangle
1586 					Vector3 newPosition(atof(argv[3]), atof(argv[4]), atof(argv[5]));
1587 					int newFacing = atoi(argv[6]);
1588 					int newHeight = atoi(argv[7]);
1589 					int newWidth =  atoi(argv[8]);
1590 					//// setXYZ recalculates the item's bounding box
1591 					//_vm->_items->setXYZ(itemId, newPosition);
1592 					//// facing affects the angle and thus the screenRect in the next tick()
1593 					_vm->_items->setFacing(itemId, newFacing);
1594 					if (_vm->_sceneObjects->remove(itemId + kSceneObjectOffsetItems)) {
1595 						float x0_new, y0_new, z0_new, x1_new, y1_new, z1_new;
1596 						bool targetable = _vm->_items->isTarget(itemId);
1597 						bool obstacle = _vm->_items->isVisible(itemId);
1598 						bool polizeMazeEnemy = _vm->_items->isPoliceMazeEnemy(itemId);
1599 						int itemAnimationId = -1;
1600 						_vm->_items->getAnimationId(itemId, &itemAnimationId);
1601 						_vm->_items->addToWorld(itemId, itemAnimationId, _vm->_scene->_setId, newPosition, newFacing, newHeight, newWidth, targetable, obstacle, polizeMazeEnemy, true);
1602 						_vm->_items->getBoundingBox(itemId).getXYZ(&x0_new, &y0_new, &z0_new, &x1_new, &y1_new, &z1_new);
1603 						debugPrintf("New Pos(%02.2f,%02.2f,%02.2f), Face: %d, Height: %d, Width: %d\n Bbox(%02.2f,%02.2f,%02.2f) ~ (%02.2f,%02.2f,%02.2f)\n",
1604 						            newPosition.x, newPosition.y, newPosition.z,
1605 						            newFacing, newHeight, newWidth,
1606 						            x0_new, y0_new, z0_new, x1_new, y1_new, z1_new);
1607 					}
1608 				} else {
1609 					// edit flags
1610 					bool newObstacle  = (atoi(argv[3]) != 0);
1611 					bool newTarget    = (atoi(argv[4]) != 0);
1612 					_vm->_items->setIsObstacle(itemId, newObstacle);
1613 					_vm->_items->setIsTarget(itemId, newTarget);
1614 					debugPrintf("Setting item %d as visible/clickable: %s and target: %s\n", itemId, newObstacle? "T":"F", newTarget? "T":"F");
1615 				}
1616 				return true;
1617 			} else {
1618 				debugPrintf("No item was found with the specified id: %d in the scene\n", itemId);
1619 				return true;
1620 			}
1621 		} else if (modeName == "spin" && argc == 3) {
1622 			int itemAnimationId = atoi(argv[2]);
1623 			if (itemAnimationId >=0 && itemAnimationId <= 996) {
1624 				_vm->_itemPickup->setup(itemAnimationId, 320, 240);
1625 				return false; // close the debugger
1626 			} else {
1627 				debugPrintf("Valid value range for item animation Ids is [0, 996]\n");
1628 				return true;
1629 			}
1630 		} else {
1631 			invalidSyntax = true;
1632 		}
1633 	}
1634 
1635 	if (invalidSyntax) {
1636 		debugPrintf("Add, edit flags, bounds or remove an item in the current scene\n");
1637 		debugPrintf("Use debugger command List with \"items\" argument to view available targets for this command\n");
1638 		debugPrintf("Floats:   posX, posY, posZ\n");
1639 		debugPrintf("Integers: id, facing, height, width, animationId\n");
1640 		debugPrintf("Boolean (1: true, 0: false): isVisible, isTarget\n");
1641 		debugPrintf("Usage 1: %s add    <id> <posX> <posY> <posZ> <facing> <height> <width> <animationId>\n", argv[0]);
1642 		debugPrintf("Usage 2: %s list   <id>\n", argv[0]);
1643 		debugPrintf("Usage 3: %s flags  <id> <isVisible> <isTarget>\n", argv[0]);
1644 		debugPrintf("Usage 4: %s bounds <id> <posX> <posY> <posZ> <facing> <height> <width>\n", argv[0]);
1645 		debugPrintf("Usage 5: %s remove <id>\n", argv[0]);
1646 		debugPrintf("Usage 6: %s spin   <animationId>\n", argv[0]);
1647 	}
1648 	return true;
1649 }
1650 
1651 /**
1652 * Add a new or remove an existing region (plain or exit) into/from a specified slot in the _regions or _exits arrays respectively
1653 */
cmdRegion(int argc,const char ** argv)1654 bool Debugger::cmdRegion(int argc, const char **argv) {
1655 	bool invalidSyntax = false;
1656 
1657 	if (argc < 4) {
1658 		invalidSyntax = true;
1659 	} else {
1660 		Common::String regionTypeName = argv[1];
1661 		regionTypeName.toLowercase();
1662 
1663 		Regions *regions = nullptr;
1664 		if (regionTypeName == "reg") {
1665 			regions = _vm->_scene->_regions;
1666 		} else if (regionTypeName == "exit") {
1667 			regions = _vm->_scene->_exits;
1668 		} else {
1669 			debugPrintf("Invalid region name type was specified: %s\n", regionTypeName.c_str());
1670 			return true;
1671 		}
1672 
1673 		Common::String modeName = argv[2];
1674 		modeName.toLowercase();
1675 		int regionID = atoi(argv[3]);
1676 		if (regionID < 0 || regionID >= 10) {
1677 			debugPrintf("A region id has to be an integer within [0, 9]\n");
1678 			return true;
1679 		}
1680 		if (modeName == "add" && ((regionTypeName == "reg" && argc == 8) || (regionTypeName == "exit" && argc == 9)) ) {
1681 			// add region mode
1682 			if (!regions->_regions[regionID].present) {
1683 				int type = 0;
1684 				int topY    = atoi(argv[4]);
1685 				int leftX   = atoi(argv[5]);
1686 				int bottomY = atoi(argv[6]);
1687 				int rightX  = atoi(argv[7]);
1688 				if (regionTypeName == "exit") {
1689 					type = atoi(argv[8]);
1690 				}
1691 				Common::Rect newRect(leftX, topY, rightX, bottomY);
1692 				regions->add(regionID, newRect, type);
1693 				debugPrintf("Adding %s: %d (t:%d l:%d b:%d r:%d) of type %d\n", regionTypeName.c_str(), regionID, newRect.top, newRect.left, newRect.bottom, newRect.right, type);
1694 				return true;
1695 			} else {
1696 				debugPrintf("There already is an %s with the specified id: %d. Please select another slot id\n", regionTypeName.c_str(), regionID);
1697 				return true;
1698 			}
1699 		} else if ((modeName == "remove" && argc == 4)
1700 		           || (modeName == "list" && argc == 4)
1701 		           || (modeName == "bounds" && argc == 8)) {
1702 			if (regions->_regions[regionID].present) {
1703 				Common::Rect origRect = regions->_regions[regionID].rectangle;
1704 				int type = regions->_regions[regionID].type;
1705 				if (modeName == "remove") {
1706 					if (regions->remove(regionID)) {
1707 						debugPrintf("Removed %s: %d (t:%d l:%d b:%d r:%d) of type: %d\n", regionTypeName.c_str(), regionID, origRect.top, origRect.left, origRect.bottom, origRect.right, type);
1708 					} else {
1709 						debugPrintf("Unable to remove %s: %d for unexpected reasons\n", regionTypeName.c_str(), regionID);
1710 					}
1711 				} else if (modeName == "bounds") {
1712 					int topY, leftX, bottomY, rightX = 0;
1713 					topY    = atoi(argv[4]);
1714 					leftX   = atoi(argv[5]);
1715 					bottomY = atoi(argv[6]);
1716 					rightX  = atoi(argv[7]);
1717 
1718 					if (regions->remove(regionID)) {
1719 						Common::Rect newRect(leftX, topY, rightX, bottomY);
1720 						regions->add(regionID, newRect, type);
1721 						debugPrintf("Bounds %s: %d (t:%d l:%d b:%d r:%d)\n", regionTypeName.c_str(), regionID, newRect.top, newRect.left, newRect.bottom, newRect.right);
1722 					}
1723 				} else {
1724 					// list properties
1725 					debugPrintf("%s: %d (t:%d l:%d b:%d r:%d) of type: %d\n", regionTypeName.c_str(), regionID, origRect.top, origRect.left, origRect.bottom, origRect.right, type);
1726 				}
1727 				return true;
1728 			} else {
1729 				debugPrintf("The %s id %d specified does not exist in the scene\n", regionTypeName.c_str(), regionID);
1730 				return true;
1731 			}
1732 		} else {
1733 			invalidSyntax = true;
1734 		}
1735 	}
1736 
1737 	if (invalidSyntax) {
1738 		debugPrintf("Add, edit bounds or remove a region (\"reg\": for plain region, \"exit\": for exit) in the current scene\n");
1739 		debugPrintf("Use debugger command List with \"reg\" argument to view available targets for this command\n");
1740 		debugPrintf("An exit type can be in [0, 3] and determines the type of arrow icon on mouse-over\n0: Upward , 1: Right, 2: Downward, 3: Left\n");
1741 		debugPrintf("Integers: id, topY, leftX, bottomY, rightX, type\n");
1742 		debugPrintf("Usage 1: %s reg  add    <id> <topY> <leftX> <bottomY> <rightX>\n", argv[0]);
1743 		debugPrintf("Usage 2: %s reg  remove <id>\n", argv[0]);
1744 		debugPrintf("Usage 3: %s reg  list   <id>\n", argv[0]);
1745 		debugPrintf("Usage 4: %s reg  bounds <id> <topY> <leftX> <bottomY> <rightX>\n", argv[0]);
1746 		debugPrintf("Usage 5: %s exit add    <id> <topY> <leftX> <bottomY> <rightX> <type>\n", argv[0]);
1747 		debugPrintf("Usage 6: %s exit remove <id>\n", argv[0]);
1748 		debugPrintf("Usage 7: %s exit list   <id>\n", argv[0]);
1749 		debugPrintf("Usage 8: %s exit bounds <id> <topY> <leftX> <bottomY> <rightX>\n", argv[0]);
1750 	}
1751 	return true;
1752 }
1753 
1754 /**
1755 * Toggle showing mouse click info in the text console (not the debugger window)
1756 */
cmdClick(int argc,const char ** argv)1757 bool Debugger::cmdClick(int argc, const char **argv) {
1758 	bool invalidSyntax = false;
1759 
1760 	if (argc != 2) {
1761 		invalidSyntax = true;
1762 	} else {
1763 		//
1764 		// set a debug variable to enable showing the mouse click info
1765 		//
1766 		Common::String argName = argv[1];
1767 		argName.toLowercase();
1768 		if (argc == 2 && argName == "toggle") {
1769 			_showMouseClickInfo = !_showMouseClickInfo;
1770 			debugPrintf("Showing mouse click info = %s\n", _showMouseClickInfo ? "True":"False");
1771 			return false; // close the debugger console
1772 		} else {
1773 			invalidSyntax = true;
1774 		}
1775 	}
1776 
1777 	if (invalidSyntax) {
1778 		debugPrintf("Toggle showing mouse info (on mouse click) in the text console\n");
1779 		debugPrintf("Usage: %s toggle\n", argv[0]);
1780 	}
1781 	return true;
1782 }
1783 
1784 /**
1785 * Auxiliary function to get a descriptive string for a given difficulty value
1786 */
getDifficultyDescription(int difficultyValue)1787 Common::String Debugger::getDifficultyDescription(int difficultyValue) {
1788 	Common::String difficultyStr;
1789 	switch (difficultyValue) {
1790 	default:
1791 		// fall through
1792 	case kGameDifficultyEasy:
1793 		difficultyStr = Common::String::format("Easy (%d)", kGameDifficultyEasy);
1794 		break;
1795 	case kGameDifficultyMedium:
1796 		difficultyStr = Common::String::format("Normal (%d)", kGameDifficultyMedium);
1797 		break;
1798 	case kGameDifficultyHard:
1799 		difficultyStr = Common::String::format("Hard (%d)", kGameDifficultyHard);
1800 		break;
1801 	}
1802 	return difficultyStr;
1803 }
1804 
1805 /**
1806 * Show or set current game's difficulty mode
1807 */
cmdDifficulty(int argc,const char ** argv)1808 bool Debugger::cmdDifficulty(int argc, const char **argv) {
1809 	bool invalidSyntax = false;
1810 
1811 	if (argc == 1) {
1812 		debugPrintf("Current game difficulty is %s\n", getDifficultyDescription(_vm->_settings->getDifficulty()).c_str());
1813 	} else if (argc == 2) {
1814 		int difficultyID = atoi(argv[1]);
1815 		if (difficultyID < kGameDifficultyEasy || difficultyID > kGameDifficultyHard) {
1816 			debugPrintf("The difficulty value must be an integer within [0, 2]\n");
1817 			return true;
1818 		} else {
1819 			_vm->_settings->setDifficulty(difficultyID);
1820 			debugPrintf("Current game difficulty is set to %s\n", getDifficultyDescription(_vm->_settings->getDifficulty()).c_str());
1821 		}
1822 	} else {
1823 		invalidSyntax = true;
1824 	}
1825 
1826 	if (invalidSyntax) {
1827 		debugPrintf("Show or set current game's difficulty mode\n");
1828 		debugPrintf("Valid difficulty values: \n");
1829 		debugPrintf("0: Easy\n");
1830 		debugPrintf("1: Normal\n");
1831 		debugPrintf("2: Hard\n");
1832 		debugPrintf("Usage 1: %s\n", argv[0]);
1833 		debugPrintf("Usage 2: %s <difficulty>\n", argv[0]);
1834 	}
1835 	return true;
1836 }
1837 #if BLADERUNNER_ORIGINAL_BUGS
1838 #else
cmdEffect(int argc,const char ** argv)1839 bool Debugger::cmdEffect(int argc, const char **argv) {
1840 	bool invalidSyntax = false;
1841 
1842 	if (argc != 3) {
1843 		invalidSyntax = true;
1844 	} else {
1845 		int effectId = atoi(argv[2]);
1846 		Common::String modeName = argv[1];
1847 		modeName.toLowercase();
1848 
1849 		if (modeName == "list") {
1850 			if (effectId >= 0 && effectId < (int)_vm->_screenEffects->_entries.size()) {
1851 				ScreenEffects::Entry &entry = _vm->_screenEffects->_entries[effectId];
1852 				debugPrintf("%2d. Effect (h: %d, x: %d, y: %d, z: %d\n", effectId, (int)entry.height, (int)entry.x, (int)entry.y, (int)entry.z);
1853 			} else {
1854 				debugPrintf("There is no such effect in the scene!\n");
1855 			}
1856 		} else if (modeName == "skip") {
1857 			if (effectId >= 0 && effectId < (int)_vm->_screenEffects->_entries.size()) {
1858 				_vm->_screenEffects->toggleEntry(effectId, true);
1859 				debugPrintf("Skipped effect %2d\n", effectId);
1860 			} else {
1861 				debugPrintf("There is no such effect to remove in the scene!\n");
1862 			}
1863 		} else if (modeName == "restore") {
1864 			if (effectId >= 0 && effectId < (int)_vm->_screenEffects->kMaxEffectsInScene) {
1865 				_vm->_screenEffects->toggleEntry(effectId, false);
1866 				debugPrintf("Attempting to restored effect %2d\n", effectId);
1867 			}
1868 		} else {
1869 			invalidSyntax = true;
1870 		}
1871 	}
1872 
1873 	if (invalidSyntax) {
1874 		debugPrintf("List properties or skip/restore a screen-effect obj in the current scene\n");
1875 		debugPrintf("Usage 1: %s  list     <id>\n", argv[0]);
1876 		debugPrintf("Usage 2: %s  (skip | restore) <id>\n", argv[0]);
1877 	}
1878 	return true;
1879 }
1880 #endif // BLADERUNNER_ORIGINAL_BUGS
1881 
1882 /**
1883 * Toggle playing a full VK session (full) and showing current test statistics as subtitles
1884 * Only available in VK mode
1885 */
cmdVk(int argc,const char ** argv)1886 bool Debugger::cmdVk(int argc, const char **argv) {
1887 	bool invalidSyntax = false;
1888 
1889 	if (argc != 2) {
1890 		invalidSyntax = true;
1891 	} else {
1892 		if (!_vm->_vk->isOpen()) {
1893 			debugPrintf("Error:Command %s is only valid within a Voigt-Kampff session\n",  argv[0]);
1894 			return true;
1895 		}
1896 		//
1897 		// set a debug variable to enable going through the (remaining) VK session
1898 		// enabling all the remaining VK questions
1899 		//
1900 		Common::String argName = argv[1];
1901 		argName.toLowercase();
1902 		if (argc == 2 && argName == "full") {
1903 			_playFullVk = !_playFullVk;
1904 			debugPrintf("Playing full V-K session = %s\n", _playFullVk ? "True":"False");
1905 		} else if (argc == 2 && argName == "stats") {
1906 			_showStatsVk = !_showStatsVk;
1907 			debugPrintf("Showing V-K session statistics= %s\n", _showStatsVk ? "True":"False");
1908 		} else {
1909 			invalidSyntax = true;
1910 		}
1911 	}
1912 
1913 	if (invalidSyntax) {
1914 		debugPrintf("Toggle playing the full VK session instead of the at most 10 questions of the vanilla mode\n");
1915 		debugPrintf("Also, toggle showing statistics for the current session\n");
1916 		debugPrintf("Usage: %s (full|stats)\n", argv[0]);
1917 	}
1918 	return true;
1919 
1920 }
1921 /**
1922 *
1923 * Similar to draw but only list items instead of drawing
1924 * Maybe keep this separate from the draw command, even though some code gets repeated here
1925 */
cmdList(int argc,const char ** argv)1926 bool Debugger::cmdList(int argc, const char **argv) {
1927 	bool invalidSyntax = false;
1928 
1929 	if (argc < 2) {
1930 		invalidSyntax = true;
1931 	} else {
1932 		Common::String arg = argv[1];
1933 		if (arg == "act") {
1934 			if (argc == 2) {
1935 				debugPrintf("Listing scene actors: \n");
1936 				int count = 0;
1937 				for (int i = 0; i < _vm->_sceneObjects->_count; i++) {
1938 					SceneObjects::SceneObject *sceneObject = &_vm->_sceneObjects->_sceneObjects[_vm->_sceneObjects->_sceneObjectsSortedByDistance[i]];
1939 
1940 					if (sceneObject->type == kSceneObjectTypeActor) {
1941 						Actor *actor = _vm->_actors[sceneObject->id - kSceneObjectOffsetActors];
1942 						debugPrintf("%d: %s (Clk: %s, Trg: %s, Prs: %s, Obs: %s, Mvg: %s)\n    Goal: %d, Set: %d, Anim mode: %d id:%d showDmg: %s inCombat: %s\n    Pos(%02.2f,%02.2f,%02.2f)\n",
1943 									 sceneObject->id - kSceneObjectOffsetActors,
1944 									 _vm->_textActorNames->getText(sceneObject->id - kSceneObjectOffsetActors),
1945 									 sceneObject->isClickable? "T" : "F",
1946 									 sceneObject->isTarget?    "T" : "F",
1947 									 sceneObject->isPresent?   "T" : "F",
1948 									 sceneObject->isObstacle?  "T" : "F",
1949 									 sceneObject->isMoving?    "T" : "F",
1950 									 actor->getGoal(),
1951 									 actor->getSetId(),
1952 									 actor->getAnimationMode(),
1953 									 actor->getAnimationId(),
1954 									 actor->getFlagDamageAnimIfMoving()? "T" : "F",
1955 									 actor->inCombat()? "T" : "F",
1956 									 actor->getPosition().x,
1957 									 actor->getPosition().y,
1958 									 actor->getPosition().z);
1959 						++count;
1960 					}
1961 				}
1962 				debugPrintf("%d actors were found in scene.\n", count);
1963 			} else if (argc == 3) {
1964 				// list properties for specific actor regardless of the set/ scene they are in
1965 				int actorId = atoi(argv[2]);
1966 				if (actorId >= 0 && actorId < _vm->kActorCount) {
1967 					debugPrintf("Showing properties for actor: %d:%s \n", actorId, _vm->_textActorNames->getText(actorId));
1968 					Actor *actor = _vm->_actors[actorId];
1969 
1970 					bool isReplicant = false;
1971 					switch (actorId) {
1972 					case kActorIzo:
1973 						isReplicant = _vm->_gameFlags->query(kFlagIzoIsReplicant);
1974 						break;
1975 					case kActorGordo:
1976 						isReplicant = _vm->_gameFlags->query(kFlagGordoIsReplicant);
1977 						break;
1978 					case kActorLucy:
1979 						isReplicant = _vm->_gameFlags->query(kFlagLucyIsReplicant);
1980 						break;
1981 					case kActorDektora:
1982 						isReplicant = _vm->_gameFlags->query(kFlagDektoraIsReplicant);
1983 						break;
1984 					case kActorSadik:
1985 						isReplicant = _vm->_gameFlags->query(kFlagSadikIsReplicant);
1986 						break;
1987 					case kActorLuther:
1988 						isReplicant = _vm->_gameFlags->query(kFlagLutherLanceIsReplicant);
1989 						break;
1990 					default:
1991 						isReplicant = false;
1992 						break;
1993 					}
1994 
1995 					debugPrintf("%d: %s (Mvg: %s, Walk:%s, Run:%s, Ret:%s, Trg: %s, Rep: %s)\n    Hp: %d, Fac: %d, Friend: %d\n    Goal: %d, Set: %d, Anim mode: %d id:%d showDmg: %s inCombat: %s\n    Pos(%02.2f,%02.2f,%02.2f), Walkbx:%d\n",
1996 						actorId,
1997 						_vm->_textActorNames->getText(actorId),
1998 						actor->isMoving()?  "T" : "F",
1999 						actor->isWalking()? "T" : "F",
2000 						actor->isRunning()? "T" : "F",
2001 						actor->isRetired()? "T" : "F",
2002 						actor->isTarget()?  "T" : "F",
2003 						isReplicant?        "T" : "F",
2004 						actor->getCurrentHP(),
2005 						actor->getFacing(),
2006 						actor->getFriendlinessToOther(kActorMcCoy),
2007 						actor->getGoal(),
2008 						actor->getSetId(),
2009 						actor->getAnimationMode(),
2010 						actor->getAnimationId(),
2011 						actor->getFlagDamageAnimIfMoving()? "T" : "F",
2012 						actor->inCombat()? "T" : "F",
2013 						actor->getPosition().x,
2014 						actor->getPosition().y,
2015 						actor->getPosition().z,
2016 						actor->getWalkbox());
2017 				} else {
2018 					debugPrintf("Invalid actor id: %d was specified\n", actorId);
2019 					return true;
2020 				}
2021 			} else {
2022 				invalidSyntax = true;
2023 			}
2024 		} else if (arg == "obj") {
2025 			debugPrintf("View info\nCamera position: (%5.2f, %5.2f, %5.2f), Viewport position: (%5.2f, %5.2f, %5.2f), FoVx: %2.2f\n",
2026 						_vm->_view->_cameraPosition.x,
2027 						_vm->_view->_cameraPosition.y,
2028 						_vm->_view->_cameraPosition.z,
2029 						_vm->_view->_viewportPosition.x,
2030 						_vm->_view->_viewportPosition.y,
2031 						_vm->_view->_viewportPosition.z,
2032 						_vm->_view->_fovX);
2033 			debugPrintf("Listing scene objects: \n");
2034 			int count = 0;
2035 			for (int i = 0; i < _vm->_sceneObjects->_count; i++) {
2036 				SceneObjects::SceneObject *sceneObject = &_vm->_sceneObjects->_sceneObjects[_vm->_sceneObjects->_sceneObjectsSortedByDistance[i]];
2037 				const BoundingBox &bbox = sceneObject->boundingBox;
2038 				Vector3 a, b;
2039 				bbox.getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z);
2040 				Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b));
2041 
2042 				if (sceneObject->type == kSceneObjectTypeUnknown) {
2043 					debugPrintf("%02d. Unknown object type\n", count);
2044 					++count;
2045 				} else if (sceneObject->type == kSceneObjectTypeObject) {
2046 					debugPrintf("%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",
2047 								 sceneObject->id - kSceneObjectOffsetObjects,
2048 								 _vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects).c_str(),
2049 								 sceneObject->isClickable? "T" : "F",
2050 								 sceneObject->isTarget?    "T" : "F",
2051 								 sceneObject->isPresent?   "T" : "F",
2052 								 sceneObject->isObstacle?  "T" : "F",
2053 								 sceneObject->isMoving?    "T" : "F",
2054 								 pos.x, pos.y, pos.z,
2055 								 a.x, a.y, a.z, b.x, b.y, b.z);
2056 					++count;
2057 				}
2058 			}
2059 			debugPrintf("%d objects were found in scene.\n", count);
2060 		} else if (arg == "item") {
2061 			debugPrintf("Listing scene items: \n");
2062 			int count = 0;
2063 			for (int i = 0; i < _vm->_sceneObjects->_count; i++) {
2064 				SceneObjects::SceneObject *sceneObject = &_vm->_sceneObjects->_sceneObjects[_vm->_sceneObjects->_sceneObjectsSortedByDistance[i]];
2065 
2066 				if (sceneObject->type == kSceneObjectTypeItem) {
2067 					const BoundingBox &bbox = sceneObject->boundingBox;
2068 					Vector3 a, b;
2069 					bbox.getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z);
2070 					//Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b));
2071 					Vector3 pos;
2072 					int currHeight, currWidth;
2073 					_vm->_items->getXYZ(sceneObject->id - kSceneObjectOffsetItems, &pos.x, &pos.y, &pos.z);
2074 					_vm->_items->getWidthHeight(sceneObject->id - kSceneObjectOffsetItems, &currWidth, &currHeight);
2075 					const Common::Rect &screenRect = _vm->_items->getScreenRectangle(sceneObject->id - kSceneObjectOffsetItems);
2076 					debugPrintf("Id %i, Pos(%02.2f,%02.2f,%02.2f), Face: %d, Height: %d, Width: %d, ScrRct(%d,%d,%d,%d)\n Clk: %s, Trg: %s, Prs: %s, Vis: %s, Mvg: %s Bbox(%02.2f,%02.2f,%02.2f)~(%02.2f,%02.2f,%02.2f)\n",
2077 								 sceneObject->id - kSceneObjectOffsetItems,
2078 								 pos.x, pos.y, pos.z,
2079 								 _vm->_items->getFacing(sceneObject->id - kSceneObjectOffsetItems), currHeight, currWidth,
2080 								 screenRect.top, screenRect.left, screenRect.bottom, screenRect.right,
2081 								 sceneObject->isClickable? "T" : "F",
2082 								 sceneObject->isTarget?    "T" : "F",
2083 								 sceneObject->isPresent?   "T" : "F",
2084 								 sceneObject->isObstacle?  "T" : "F",
2085 								 sceneObject->isMoving?    "T" : "F",
2086 								 a.x, a.y, a.z, b.x, b.y, b.z);
2087 					++count;
2088 				}
2089 			}
2090 			debugPrintf("%d items were found in scene.\n", count);
2091 		} else if (arg == "reg") {
2092 			debugPrintf("Listing plain regions: \n");
2093 			int count = 0;
2094 			//list regions
2095 			for (int i = 0; i < 10; i++) {
2096 				Regions::Region *region = &_vm->_scene->_regions->_regions[i];
2097 				if (!region->present) continue;
2098 				Common::Rect r = region->rectangle;
2099 				debugPrintf("Region slot: %d (t:%d l:%d b:%d r:%d)\n", i, r.top, r.left, r.bottom, r.right);
2100 				++count;
2101 			}
2102 
2103 			debugPrintf("Listing exits: \n");
2104 			//list exits
2105 			for (int i = 0; i < 10; i++) {
2106 				Regions::Region *region = &_vm->_scene->_exits->_regions[i];
2107 				if (!region->present) continue;
2108 				Common::Rect r = region->rectangle;
2109 				debugPrintf("Exit slot: %d (t:%d l:%d b:%d r:%d)\n", i, r.top, r.left, r.bottom, r.right);
2110 				++count;
2111 			}
2112 			debugPrintf("%d regions (plain and exits) were found in scene.\n", count);
2113 		} else if (arg == "way") {
2114 			debugPrintf("Listing waypoints: \n");
2115 			int count = 0;
2116 			for (int i = 0; i < _vm->_waypoints->_count; i++) {
2117 				Waypoints::Waypoint *waypoint = &_vm->_waypoints->_waypoints[i];
2118 				if (waypoint->setId != _vm->_scene->getSetId()) {
2119 					continue;
2120 				}
2121 				char waypointText[40];
2122 				Vector3 a = waypoint->position;
2123 				sprintf(waypointText, "Waypoint %i, Pos(%02.2f,%02.2f,%02.2f)", i, a.x, a.y, a.z);
2124 				debugPrintf("%s\n", waypointText);
2125 				++count;
2126 			}
2127 
2128 			// list combat cover waypoints
2129 			for (int i = 0; i < (int)_vm->_combat->_coverWaypoints.size(); i++) {
2130 				Combat::CoverWaypoint *cover = &_vm->_combat->_coverWaypoints[i];
2131 				if (cover->setId != _vm->_scene->getSetId()) {
2132 					continue;
2133 				}
2134 				char coverText[40];
2135 				Vector3 a = cover->position;
2136 				sprintf(coverText, "Cover %i, Pos(%02.2f,%02.2f,%02.2f)", i, a.x, a.y, a.z);
2137 				debugPrintf("%s\n", coverText);
2138 				++count;
2139 			}
2140 
2141 			// list combat flee waypoints
2142 			for (int i = 0; i < (int)_vm->_combat->_fleeWaypoints.size(); i++) {
2143 				Combat::FleeWaypoint *flee = &_vm->_combat->_fleeWaypoints[i];
2144 				if (flee->setId != _vm->_scene->getSetId()) {
2145 					continue;
2146 				}
2147 				char fleeText[40];
2148 				Vector3 a = flee->position;
2149 				sprintf(fleeText, "Flee %i, Pos(%02.2f,%02.2f,%02.2f)", i, a.x, a.y, a.z);
2150 				debugPrintf("%s\n", fleeText);
2151 				++count;
2152 			}
2153 			debugPrintf("%d waypoints were found in scene.\n", count);
2154 		} else if (arg == "walk") {
2155 			debugPrintf("Listing walkboxes: \n");
2156 			// list walkboxes
2157 			for (int i = 0; i < _vm->_scene->_set->_walkboxCount; i++) {
2158 				Set::Walkbox *walkbox = &_vm->_scene->_set->_walkboxes[i];
2159 
2160 				debugPrintf("%2d. Walkbox %s, vertices: %d\n", i, walkbox->name.c_str(), walkbox->vertexCount);
2161 			}
2162 			debugPrintf("%d walkboxes were found in scene.\n", _vm->_scene->_set->_walkboxCount);
2163 		} else if (arg == "fog") {
2164 			debugPrintf("Listing fogs: \n");
2165 			int count = 0;
2166 			for (Fog *fog = _vm->_scene->_set->_effects->_fogs; fog != nullptr; fog = fog->_next) {
2167 				debugPrintf("%2d. Fog %s\n", count, fog->_name.c_str());
2168 				++count;
2169 			}
2170 			debugPrintf("%d fogs were found in scene.\n", count);
2171 		} else if (arg == "lit") {
2172 			debugPrintf("Listing lights: \n");
2173 			// list lights
2174 			for (int i = 0; i < (int)_vm->_lights->_lights.size(); i++) {
2175 				Light *light = _vm->_lights->_lights[i];
2176 				debugPrintf("%2d. Light %s\n", i, light->_name.c_str());
2177 			}
2178 			debugPrintf("%d lights were found in scene.\n", (int)_vm->_lights->_lights.size());
2179 		} else if (arg == "eff") {
2180 			debugPrintf("Listing scene effects: \n");
2181 			// list scene effects
2182 			for (uint i = 0; i < _vm->_screenEffects->_entries.size(); i++) {
2183 				ScreenEffects::Entry &entry = _vm->_screenEffects->_entries[i];
2184 				debugPrintf("%2d. Effect (h: %d, x: %d, y: %d, z: %d\n", i, (int)entry.height, (int)entry.x, (int)entry.y, (int)entry.z);
2185 			}
2186 			debugPrintf("%d scene effects were found in scene.\n", (int)_vm->_screenEffects->_entries.size());
2187 		} else {
2188 			debugPrintf("Invalid item type was specified.\n");
2189 		}
2190 	}
2191 
2192 	if (invalidSyntax) {
2193 		debugPrintf("Enables debug listing of actors, scene objects, items, waypoints, regions, lights, fogs and walk-boxes.\n");
2194 		debugPrintf("Usage 1: %s (act | obj | item | way | reg | eff | lit | fog | walk )\n", argv[0]);
2195 		debugPrintf("Usage 2: %s act <actorId>\n", argv[0]);
2196 	}
2197 	return true;
2198 }
2199 
2200 
drawDebuggerOverlay()2201 void Debugger::drawDebuggerOverlay() {
2202 
2203 	updateTogglesForDbgDrawListInCurrentSetAndScene();
2204 
2205 	if (_viewActorsToggle || _specificActorsDrawn
2206 	    || _view3dObjectsToggle || _specific3dObjectsDrawn
2207 	    || _viewItemsToggle || _specificItemsDrawn
2208 	) {
2209 		drawSceneObjects();
2210 	}
2211 	if (_viewScreenEffects || _specificEffectsDrawn) drawScreenEffects();
2212 	if (_viewLights || _specificLightsDrawn) drawLights();
2213 	if (_viewFogs || _specificFogsDrawn) drawFogs();
2214 	if (_viewRegionsNormalToggle || _specificRegionNormalDrawn
2215 	    || _viewRegionsExitsToggle || _specificRegionExitsDrawn
2216 	) {
2217 		drawRegions();
2218 	}
2219 	if (_viewWaypointsNormalToggle || _specificWaypointNormalDrawn
2220 	    || _viewWaypointsFleeToggle || _specificWaypointFleeDrawn
2221 	    || _viewWaypointsCoverToggle || _specificWaypointCoverDrawn
2222 	) {
2223 		drawWaypoints();
2224 	}
2225 	if (_viewWalkboxes || _specificWalkboxesDrawn) drawWalkboxes();
2226 }
2227 
drawBBox(Vector3 start,Vector3 end,View * view,Graphics::Surface * surface,int color)2228 void Debugger::drawBBox(Vector3 start, Vector3 end, View *view, Graphics::Surface *surface, int color) {
2229 	Vector3 bfl = view->calculateScreenPosition(Vector3(start.x, start.y, start.z));
2230 	Vector3 bfr = view->calculateScreenPosition(Vector3(start.x, end.y, start.z));
2231 	Vector3 bbr = view->calculateScreenPosition(Vector3(end.x, end.y, start.z));
2232 	Vector3 bbl = view->calculateScreenPosition(Vector3(end.x, start.y, start.z));
2233 
2234 	Vector3 tfl = view->calculateScreenPosition(Vector3(start.x, start.y, end.z));
2235 	Vector3 tfr = view->calculateScreenPosition(Vector3(start.x, end.y, end.z));
2236 	Vector3 tbr = view->calculateScreenPosition(Vector3(end.x, end.y, end.z));
2237 	Vector3 tbl = view->calculateScreenPosition(Vector3(end.x, start.y, end.z));
2238 
2239 	surface->drawLine(bfl.x, bfl.y, bfr.x, bfr.y, color);
2240 	surface->drawLine(bfr.x, bfr.y, bbr.x, bbr.y, color);
2241 	surface->drawLine(bbr.x, bbr.y, bbl.x, bbl.y, color);
2242 	surface->drawLine(bbl.x, bbl.y, bfl.x, bfl.y, color);
2243 
2244 	surface->drawLine(tfl.x, tfl.y, tfr.x, tfr.y, color);
2245 	surface->drawLine(tfr.x, tfr.y, tbr.x, tbr.y, color);
2246 	surface->drawLine(tbr.x, tbr.y, tbl.x, tbl.y, color);
2247 	surface->drawLine(tbl.x, tbl.y, tfl.x, tfl.y, color);
2248 
2249 	surface->drawLine(bfl.x, bfl.y, tfl.x, tfl.y, color);
2250 	surface->drawLine(bfr.x, bfr.y, tfr.x, tfr.y, color);
2251 	surface->drawLine(bbr.x, bbr.y, tbr.x, tbr.y, color);
2252 	surface->drawLine(bbl.x, bbl.y, tbl.x, tbl.y, color);
2253 }
2254 
drawSceneObjects()2255 void Debugger::drawSceneObjects() {
2256 	//draw scene objects
2257 	int count = _vm->_sceneObjects->_count;
2258 	if (count > 0) {
2259 		for (int i = 0; i < count; i++) {
2260 			SceneObjects::SceneObject *sceneObject = &_vm->_sceneObjects->_sceneObjects[_vm->_sceneObjects->_sceneObjectsSortedByDistance[i]];
2261 
2262 			const BoundingBox &bbox = sceneObject->boundingBox;
2263 			Vector3 a, b;
2264 			bbox.getXYZ(&a.x, &a.y, &a.z, &b.x, &b.y, &b.z);
2265 			Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (a + b));
2266 			int color;
2267 
2268 			switch (sceneObject->type) {
2269 			case kSceneObjectTypeUnknown:
2270 				break;
2271 			case kSceneObjectTypeActor:
2272 				if (_viewActorsToggle
2273 				    || (_specificActorsDrawn && findInDbgDrawList(debuggerObjTypeActor, sceneObject->id - kSceneObjectOffsetActors, -1, -1) != -1)
2274 				) {
2275 					color = _vm->_surfaceFront.format.RGBToColor(255, 0, 0);
2276 					drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
2277 					_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
2278 					_vm->_mainFont->drawString(&_vm->_surfaceFront, _vm->_textActorNames->getText(sceneObject->id - kSceneObjectOffsetActors), pos.x, pos.y, _vm->_surfaceFront.w, color);
2279 				}
2280 				break;
2281 			case kSceneObjectTypeItem:
2282 				if (_viewItemsToggle
2283 				    || (_specificItemsDrawn && findInDbgDrawList(debuggerObjTypeItem, sceneObject->id - kSceneObjectOffsetItems, -1, -1) != -1)
2284 				) {
2285 					color = _vm->_surfaceFront.format.RGBToColor(0, 255, 0);
2286 					char itemText[40];
2287 					drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
2288 					sprintf(itemText, "item %i", sceneObject->id - kSceneObjectOffsetItems);
2289 					_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
2290 					_vm->_mainFont->drawString(&_vm->_surfaceFront, itemText, pos.x, pos.y, _vm->_surfaceFront.w, color);
2291 				}
2292 				break;
2293 			case kSceneObjectTypeObject:
2294 				if (_view3dObjectsToggle
2295 				    || (_specific3dObjectsDrawn && findInDbgDrawList(debuggerObjType3dObject, sceneObject->id - kSceneObjectOffsetObjects, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2296 				) {
2297 					color = _vm->_surfaceFront.format.RGBToColor(127, 127, 127);
2298 					if (sceneObject->isClickable) {
2299 						color = _vm->_surfaceFront.format.RGBToColor(0, 255, 0);
2300 					}
2301 					drawBBox(a, b, _vm->_view, &_vm->_surfaceFront, color);
2302 					_vm->_surfaceFront.frameRect(sceneObject->screenRectangle, color);
2303 					_vm->_mainFont->drawString(&_vm->_surfaceFront, _vm->_scene->objectGetName(sceneObject->id - kSceneObjectOffsetObjects), pos.x, pos.y, _vm->_surfaceFront.w, color);
2304 				}
2305 				break;
2306 			}
2307 		}
2308 	}
2309 }
2310 
drawLights()2311 void Debugger::drawLights() {
2312 	// draw lights
2313 	for (int i = 0; i < (int)_vm->_lights->_lights.size(); i++) {
2314 		if (_viewLights
2315 		    || (_specificLightsDrawn && findInDbgDrawList(debuggerObjTypeLight, i, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2316 		) {
2317 			Light *light = _vm->_lights->_lights[i];
2318 			Matrix4x3 m = light->_matrix;
2319 			m = invertMatrix(m);
2320 			Vector3 posOrigin = m * Vector3(0.0f, 0.0f, 0.0f);
2321 			float t = posOrigin.y;
2322 			posOrigin.y = posOrigin.z;
2323 			posOrigin.z = -t;
2324 
2325 			Vector3 posTarget = m * Vector3(0.0f, 0.0f, -100.0f);
2326 			t = posTarget.y;
2327 			posTarget.y = posTarget.z;
2328 			posTarget.z = -t;
2329 
2330 			Vector3 size = Vector3(5.0f, 5.0f, 5.0f);
2331 			int color = _vm->_surfaceFront.format.RGBToColor(light->_color.r * 255.0f, light->_color.g * 255.0f, light->_color.b * 255.0f);
2332 
2333 			drawBBox(posOrigin - size, posOrigin + size, _vm->_view, &_vm->_surfaceFront, color);
2334 
2335 			Vector3 posOriginT = _vm->_view->calculateScreenPosition(posOrigin);
2336 			Vector3 posTargetT = _vm->_view->calculateScreenPosition(posTarget);
2337 
2338 			_vm->_surfaceFront.drawLine(posOriginT.x, posOriginT.y, posTargetT.x, posTargetT.y, color);
2339 
2340 			_vm->_mainFont->drawString(&_vm->_surfaceFront, light->_name, posOriginT.x, posOriginT.y, _vm->_surfaceFront.w, color);
2341 		}
2342 	}
2343 }
2344 
drawFogs()2345 void Debugger::drawFogs() {
2346 	Fog *fog = _vm->_scene->_set->_effects->_fogs;
2347 	for (int i = 0; fog != nullptr; ++i) {
2348 		if (_viewFogs
2349 		    || (_specificFogsDrawn && findInDbgDrawList(debuggerObjTypeFog, i, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2350 		) {
2351 			// Matrix4x3 m = fog->_matrix;
2352 			// m = invertMatrix(m);
2353 			Matrix4x3 m = fog->_inverted;
2354 
2355 			Vector3 posOrigin = m * Vector3(0.0f, 0.0f, 0.0f);
2356 			float t = posOrigin.y;
2357 			posOrigin.y = posOrigin.z;
2358 			posOrigin.z = -t;
2359 
2360 			Vector3 posTarget = m * Vector3(0.0f, 0.0f, -100.0f);
2361 			t = posTarget.y;
2362 			posTarget.y = posTarget.z;
2363 			posTarget.z = -t;
2364 
2365 			Vector3 size = Vector3(5.0f, 5.0f, 5.0f);
2366 			int color = _vm->_surfaceFront.format.RGBToColor(fog->_fogColor.r * 255.0f, fog->_fogColor.g * 255.0f, fog->_fogColor.b * 255.0f);
2367 
2368 			drawBBox(posOrigin - size, posOrigin + size, _vm->_view, &_vm->_surfaceFront, color);
2369 
2370 			Vector3 posOriginT = _vm->_view->calculateScreenPosition(posOrigin);
2371 			Vector3 posTargetT = _vm->_view->calculateScreenPosition(posTarget);
2372 
2373 			// TODO: draw line only for cone fogs, draw boxes or circles for the other types
2374 			_vm->_surfaceFront.drawLine(posOriginT.x, posOriginT.y, posTargetT.x, posTargetT.y, color);
2375 
2376 			_vm->_mainFont->drawString(&_vm->_surfaceFront, fog->_name, posOriginT.x, posOriginT.y, _vm->_surfaceFront.w, color);
2377 		}
2378 		fog = fog->_next;
2379 	}
2380 }
2381 
drawRegions()2382 void Debugger::drawRegions() {
2383 	if (_viewRegionsNormalToggle || _specificRegionNormalDrawn) {
2384 		//draw regions
2385 		for (int i = 0; i < 10; i++) {
2386 			Regions::Region *region = &_vm->_scene->_regions->_regions[i];
2387 			if (!region->present) continue;
2388 			if (_viewRegionsNormalToggle
2389 				|| (_specificRegionNormalDrawn && findInDbgDrawList(debuggerObjTypeRegionNormal, i, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2390 			) {
2391 				_vm->_surfaceFront.frameRect(region->rectangle, _vm->_surfaceFront.format.RGBToColor(0, 0, 255));
2392 			}
2393 		}
2394 	}
2395 
2396 	if (_viewRegionsExitsToggle || _specificRegionExitsDrawn) {
2397 		//draw exits
2398 		for (int i = 0; i < 10; i++) {
2399 			Regions::Region *region = &_vm->_scene->_exits->_regions[i];
2400 			if (!region->present) continue;
2401 			if (_viewRegionsExitsToggle
2402 				|| (_specificRegionExitsDrawn && findInDbgDrawList(debuggerObjTypeRegionExit, i, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2403 			) {
2404 				_vm->_surfaceFront.frameRect(region->rectangle, _vm->_surfaceFront.format.RGBToColor(255, 255, 255));
2405 			}
2406 		}
2407 	}
2408 }
2409 
drawWaypoints()2410 void Debugger::drawWaypoints() {
2411 	if (_viewWaypointsNormalToggle || _specificWaypointNormalDrawn)  {
2412 		//draw world waypoints
2413 		for (int i = 0; i < _vm->_waypoints->_count; i++) {
2414 			Waypoints::Waypoint *waypoint = &_vm->_waypoints->_waypoints[i];
2415 			if (waypoint->setId != _vm->_scene->getSetId()) {
2416 				continue;
2417 			}
2418 			if (_viewWaypointsNormalToggle
2419 				|| (_specificWaypointNormalDrawn && findInDbgDrawList(debuggerObjTypeWaypointNorm, i, -1, -1) != -1)
2420 			) {
2421 				Vector3 pos = waypoint->position;
2422 				Vector3 size = Vector3(3.0f, 3.0f, 3.0f);
2423 				int color = _vm->_surfaceFront.format.RGBToColor(255, 255, 255);
2424 				drawBBox(pos - size, pos + size, _vm->_view, &_vm->_surfaceFront, color);
2425 				Vector3 spos = _vm->_view->calculateScreenPosition(pos);
2426 				char waypointText[40];
2427 				sprintf(waypointText, "waypoint %i", i);
2428 				_vm->_mainFont->drawString(&_vm->_surfaceFront, waypointText, spos.x, spos.y, _vm->_surfaceFront.w, color);
2429 			}
2430 		}
2431 	}
2432 
2433 	if (_viewWaypointsCoverToggle || _specificWaypointCoverDrawn) {
2434 		//draw combat cover waypoints
2435 		for (int i = 0; i < (int)_vm->_combat->_coverWaypoints.size(); i++) {
2436 			Combat::CoverWaypoint *cover = &_vm->_combat->_coverWaypoints[i];
2437 			if (cover->setId != _vm->_scene->getSetId()) {
2438 				continue;
2439 			}
2440 			if (_viewWaypointsCoverToggle
2441 				|| (_specificWaypointCoverDrawn && findInDbgDrawList(debuggerObjTypeWaypointCover, i, -1, -1) != -1)
2442 			) {
2443 				Vector3 pos = cover->position;
2444 				Vector3 size = Vector3(3.0f, 3.0f, 3.0f);
2445 				int color = _vm->_surfaceFront.format.RGBToColor(255, 0, 255);
2446 				drawBBox(pos - size, pos + size, _vm->_view, &_vm->_surfaceFront, color);
2447 				Vector3 spos = _vm->_view->calculateScreenPosition(pos);
2448 				char coverText[40];
2449 				sprintf(coverText, "cover %i", i);
2450 				_vm->_mainFont->drawString(&_vm->_surfaceFront, coverText, spos.x, spos.y, _vm->_surfaceFront.w, color);
2451 			}
2452 		}
2453 	}
2454 
2455 	if (_viewWaypointsFleeToggle || _specificWaypointFleeDrawn) {
2456 		//draw combat flee waypoints
2457 		for (int i = 0; i < (int)_vm->_combat->_fleeWaypoints.size(); i++) {
2458 			Combat::FleeWaypoint *flee = &_vm->_combat->_fleeWaypoints[i];
2459 			if (flee->setId != _vm->_scene->getSetId()) {
2460 				continue;
2461 			}
2462 			if (_viewWaypointsFleeToggle
2463 				|| (_specificWaypointFleeDrawn && findInDbgDrawList(debuggerObjTypeWaypoingFlee, i, -1, -1) != -1)
2464 			) {
2465 				Vector3 pos = flee->position;
2466 				Vector3 size = Vector3(3.0f, 3.0f, 3.0f);
2467 				int color = _vm->_surfaceFront.format.RGBToColor(0, 255, 255);
2468 				drawBBox(pos - size, pos + size, _vm->_view, &_vm->_surfaceFront, color);
2469 				Vector3 spos = _vm->_view->calculateScreenPosition(pos);
2470 				char fleeText[40];
2471 				sprintf(fleeText, "flee %i", i);
2472 				_vm->_mainFont->drawString(&_vm->_surfaceFront, fleeText, spos.x, spos.y, _vm->_surfaceFront.w, color);
2473 			}
2474 		}
2475 	}
2476 }
2477 
drawWalkboxes()2478 void Debugger::drawWalkboxes() {
2479 	//draw walkboxes
2480 	for (int i = 0; i < _vm->_scene->_set->_walkboxCount; i++) {
2481 		if (_viewWalkboxes
2482 		    || (_specificWalkboxesDrawn && findInDbgDrawList(debuggerObjTypeWalkbox, i, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2483 		) {
2484 			Set::Walkbox *walkbox = &_vm->_scene->_set->_walkboxes[i];
2485 			for (int j = 0; j < walkbox->vertexCount; j++) {
2486 				Vector3 start = _vm->_view->calculateScreenPosition(walkbox->vertices[j]);
2487 				Vector3 end = _vm->_view->calculateScreenPosition(walkbox->vertices[(j + 1) % walkbox->vertexCount]);
2488 				_vm->_surfaceFront.drawLine(start.x, start.y, end.x, end.y, _vm->_surfaceFront.format.RGBToColor(255, 255, 0));
2489 				Vector3 pos = _vm->_view->calculateScreenPosition(0.5 * (walkbox->vertices[j] + walkbox->vertices[(j + 1) % walkbox->vertexCount]));
2490 				_vm->_mainFont->drawString(&_vm->_surfaceFront, walkbox->name, pos.x, pos.y, _vm->_surfaceFront.w, _vm->_surfaceFront.format.RGBToColor(255, 255, 0));
2491 			}
2492 		}
2493 	}
2494 }
2495 
drawScreenEffects()2496 void Debugger::drawScreenEffects() {
2497 	//draw aesc
2498 	for (uint i = 0; i < _vm->_screenEffects->_entries.size(); i++) {
2499 		if (_viewScreenEffects
2500 		    || (_specificEffectsDrawn && findInDbgDrawList(debuggerObjTypeEffect, i, _vm->_scene->getSetId(), _vm->_scene->getSceneId()) != -1)
2501 		) {
2502 			ScreenEffects::Entry &entry = _vm->_screenEffects->_entries[i];
2503 			int j = 0;
2504 			for (int y = 0; y < entry.height; y++) {
2505 				for (int x = 0; x < entry.width; x++) {
2506 					Common::Rect r((entry.x + x) * 2, (entry.y + y) * 2, (entry.x + x) * 2 + 2, (entry.y + y) * 2 + 2);
2507 
2508 					int ec = entry.data[j++];
2509 					const int bladeToScummVmConstant = 256 / 16;
2510 
2511 					int color = _vm->_surfaceFront.format.RGBToColor(
2512 						CLIP(entry.palette[ec].r * bladeToScummVmConstant, 0, 255),
2513 						CLIP(entry.palette[ec].g * bladeToScummVmConstant, 0, 255),
2514 						CLIP(entry.palette[ec].b * bladeToScummVmConstant, 0, 255));
2515 					_vm->_surfaceFront.fillRect(r, color);
2516 				}
2517 			}
2518 		}
2519 	}
2520 }
2521 
toggleObjectInDbgDrawList(DebuggerDrawnObject & drObj)2522 void Debugger::toggleObjectInDbgDrawList(DebuggerDrawnObject &drObj) {
2523 	if (drObj.type == debuggerObjTypeUndefined || drObj.objId < 0) {
2524 		return;
2525 	}
2526 
2527 	//  Check if there already exists such object in the list:
2528 	//  if it exists then do a remove action
2529 	//  else do a push_back action (provided that size() < kMaxSpecificObjectsDrawnCount)
2530 	int foundAt = findInDbgDrawList(drObj.type, drObj.objId, drObj.setId, drObj.sceneId);
2531 
2532 	if (foundAt >= 0) {
2533 		_specificDrawnObjectsList.remove_at(foundAt);
2534 	} else {
2535 		if (_specificDrawnObjectsList.size() < kMaxSpecificObjectsDrawnCount) {
2536 			// TODO check if there actually is an object of such an ID in the current scene!
2537 			_specificDrawnObjectsList.push_back(drObj);
2538 		} else {
2539 			debugPrintf("The specific drawn objects list is full. Try running a draw reset or explicitly removing objects from it\n");
2540 		}
2541 	}
2542 }
2543 
2544 /**
2545 * drObjType can be a valid object type, or debuggerObjTypeUndefined as a wildcard for all object types
2546 * drObjId can be a valid object id or -1 as a wildcard
2547 * drObjSetId can be a valid Set id or -1 as a wildcard
2548 * drObjSceneId can be a valid Scene id or -1 as a wildcard
2549 * return the position in the list, if the specific drawn objects list contains the object of the specified type and id
2550 *        and it belongs to the current set and scene,
2551 *        or -1 otherwise
2552 */
findInDbgDrawList(DebuggerDrawnObjectType drObjType,int drObjId,int drObjSetId,int drObjSceneId)2553 int Debugger::findInDbgDrawList(DebuggerDrawnObjectType drObjType, int drObjId, int drObjSetId, int drObjSceneId) {
2554 	if (_specificDrawnObjectsList.empty()) {
2555 		return -1;
2556 	}
2557 
2558 	for (int i = 0; i < (int) _specificDrawnObjectsList.size(); ++i) {
2559 		if ((drObjType == debuggerObjTypeUndefined || drObjType == _specificDrawnObjectsList[i].type)
2560 			&& (drObjId == -1 || drObjId == _specificDrawnObjectsList[i].objId)
2561 		    && (drObjSetId == -1 || _specificDrawnObjectsList[i].setId == -1 || drObjSetId == _specificDrawnObjectsList[i].setId)
2562 		    && (drObjSceneId == -1 || _specificDrawnObjectsList[i].sceneId == -1 || drObjSceneId == _specificDrawnObjectsList[i].sceneId)
2563 		) {
2564 			// TODO for actors, 3d objects, items and waypoints it's probably preferable to ignore the sceneId (?)
2565 			return i;
2566 		}
2567 	}
2568 	return -1;
2569 }
2570 
updateTogglesForDbgDrawListInCurrentSetAndScene()2571 void Debugger::updateTogglesForDbgDrawListInCurrentSetAndScene() {
2572 	_specificActorsDrawn = false;
2573 	_specific3dObjectsDrawn = false;
2574 	_specificItemsDrawn = false;
2575 	_specificEffectsDrawn = false;
2576 	_specificLightsDrawn = false;
2577 	_specificFogsDrawn = false;
2578 	_specificRegionNormalDrawn = false;
2579 	_specificRegionExitsDrawn = false;
2580 	_specificWaypointNormalDrawn = false;
2581 	_specificWaypointFleeDrawn = false;
2582 	_specificWaypointCoverDrawn = false;
2583 	_specificWalkboxesDrawn = false;
2584 	for (int i = 0; i < (int) _specificDrawnObjectsList.size(); ++i) {
2585 		if ((_specificDrawnObjectsList[i].sceneId == -1 ||  _specificDrawnObjectsList[i].sceneId == _vm->_scene->getSceneId() )
2586 		    && (_specificDrawnObjectsList[i].setId == -1 || _specificDrawnObjectsList[i].setId == _vm->_scene->getSetId())
2587 		) {
2588 			switch (_specificDrawnObjectsList[i].type) {
2589 			case debuggerObjTypeActor:
2590 				_specificActorsDrawn = true;
2591 				break;
2592 			case debuggerObjType3dObject:
2593 				_specific3dObjectsDrawn = true;
2594 				break;
2595 			case debuggerObjTypeItem:
2596 				_specificItemsDrawn = true;
2597 				break;
2598 			case debuggerObjTypeRegionNormal:
2599 				_specificRegionNormalDrawn = true;
2600 				break;
2601 			case debuggerObjTypeRegionExit:
2602 				_specificRegionExitsDrawn = true;
2603 				break;
2604 			case debuggerObjTypeWaypointNorm:
2605 				_specificWaypointNormalDrawn = true;
2606 				break;
2607 			case debuggerObjTypeWaypoingFlee:
2608 				_specificWaypointFleeDrawn = true;
2609 				break;
2610 			case debuggerObjTypeWaypointCover:
2611 				_specificWaypointCoverDrawn = true;
2612 				break;
2613 			case debuggerObjTypeWalkbox:
2614 				_specificWalkboxesDrawn = true;
2615 				break;
2616 			case debuggerObjTypeEffect:
2617 				_specificEffectsDrawn = true;
2618 				break;
2619 			case debuggerObjTypeLight:
2620 				_specificLightsDrawn = true;
2621 				break;
2622 			case debuggerObjTypeFog:
2623 				_specificFogsDrawn = true;
2624 				break;
2625 			default:
2626 				break;
2627 			}
2628 		}
2629 	}
2630 	_isDebuggerOverlay = _viewActorsToggle || _specificActorsDrawn
2631 					 || _view3dObjectsToggle || _specific3dObjectsDrawn
2632 					 || _viewItemsToggle || _specificItemsDrawn
2633 					 || _viewRegionsNormalToggle || _specificRegionNormalDrawn
2634 					 || _viewRegionsExitsToggle || _specificRegionExitsDrawn
2635 					 || _viewScreenEffects || _specificEffectsDrawn
2636 					 || _viewLights || _specificLightsDrawn
2637 					 || _viewFogs || _specificFogsDrawn
2638 					 || _viewWaypointsNormalToggle || _specificWaypointNormalDrawn
2639 					 || _viewWaypointsFleeToggle || _specificWaypointFleeDrawn
2640 					 || _viewWaypointsCoverToggle || _specificWaypointCoverDrawn
2641 					 || _viewWalkboxes || _specificWalkboxesDrawn
2642 					 || !_specificDrawnObjectsList.empty();
2643 }
2644 
2645 } // End of namespace BladeRunner
2646