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