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