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 "common/system.h"
24 #include "common/config-manager.h"
25 #include "common/textconsole.h"
26
27 #include "parallaction/parallaction.h"
28 #include "parallaction/exec.h"
29 #include "parallaction/input.h"
30 #include "parallaction/parser.h"
31 #include "parallaction/saveload.h"
32 #include "parallaction/sound.h"
33 #include "parallaction/walk.h"
34
35
36 namespace Parallaction {
37
38 #define INITIAL_FREE_SARCOPHAGUS_SLOT_X 200
39
40
41 class LocationName {
42
43 Common::String _slide;
44 Common::String _character;
45 Common::String _location;
46
47 bool _hasCharacter;
48 bool _hasSlide;
49 Common::String _buf;
50
51 public:
LocationName()52 LocationName() {
53 _hasSlide = false;
54 _hasCharacter = false;
55 }
56
57 void bind(const char*);
58
location() const59 const char *location() const {
60 return _location.c_str();
61 }
62
hasCharacter() const63 bool hasCharacter() const {
64 return _hasCharacter;
65 }
66
character() const67 const char *character() const {
68 return _character.c_str();
69 }
70
hasSlide() const71 bool hasSlide() const {
72 return _hasSlide;
73 }
74
slide() const75 const char *slide() const {
76 return _slide.c_str();
77 }
78
c_str() const79 const char *c_str() const {
80 return _buf.c_str();
81 }
82 };
83
84
85
86 /*
87 bind accept the following input formats:
88
89 1 - [S].slide.[L]{.[C]}
90 2 - [L]{.[C]}
91
92 where:
93
94 [S] is the slide to be shown
95 [L] is the location to switch to (immediately in case 2, or right after slide [S] in case 1)
96 [C] is the character to be selected, and is optional
97
98 The routine tells one form from the other by searching for the '.slide.'
99
100 NOTE: there exists one script in which [L] is not used in the case 1, but its use
101 is commented out, and would definitely crash the current implementation.
102 */
bind(const char * s)103 void LocationName::bind(const char *s) {
104 _buf = s;
105 _hasSlide = false;
106 _hasCharacter = false;
107
108 Common::StringArray list;
109 char *tok = strtok(_buf.begin(), ".");
110 while (tok) {
111 list.push_back(tok);
112 tok = strtok(NULL, ".");
113 }
114
115 if (list.size() < 1 || list.size() > 4)
116 error("changeLocation: ill-formed location name '%s'", s);
117
118 if (list.size() > 1) {
119 if (list[1] == "slide") {
120 _hasSlide = true;
121 _slide = list[0];
122
123 list.remove_at(0); // removes slide name
124 list.remove_at(0); // removes 'slide'
125 }
126
127 if (list.size() == 2) {
128 _hasCharacter = true;
129 _character = list[1];
130 }
131 }
132
133 _location = list[0];
134 _buf = s; // kept as reference
135 }
136
Parallaction_ns(OSystem * syst,const PARALLACTIONGameDescription * gameDesc)137 Parallaction_ns::Parallaction_ns(OSystem* syst, const PARALLACTIONGameDescription *gameDesc) : Parallaction(syst, gameDesc),
138 _locationParser(0), _programParser(0), _walker(0) {
139 _soundManI = 0;
140 _score = 0;
141 _inTestResult = 0;
142 _callables = 0;
143 num_foglie = 0;
144 _sarcophagusDeltaX = 0;
145 _movingSarcophagus = 0;
146 _freeSarcophagusSlotX = 0;
147 _intro = 0;
148
149 _testResultLabels[0] = 0;
150 _testResultLabels[1] = 0;
151 }
152
init()153 Common::Error Parallaction_ns::init() {
154
155 _screenWidth = 320;
156 _screenHeight = 200;
157
158 if (getPlatform() == Common::kPlatformDOS) {
159 _disk = new DosDisk_ns(this);
160 } else {
161 if (getFeatures() & GF_DEMO) {
162 strcpy(_location._name, "fognedemo");
163 }
164 _disk = new AmigaDisk_ns(this);
165 }
166
167 _disk->init();
168
169 if (getPlatform() == Common::kPlatformDOS) {
170 _soundManI = new DosSoundMan_ns(this);
171 _soundManI->setMusicVolume(ConfMan.getInt("music_volume"));
172 } else {
173 _soundManI = new AmigaSoundMan_ns(this);
174 }
175
176 _soundMan = new SoundMan(_soundManI);
177
178 initResources();
179 initFonts();
180 _locationParser = new LocationParser_ns(this);
181 _locationParser->init();
182 _programParser = new ProgramParser_ns(this);
183 _programParser->init();
184
185 _cmdExec = new CommandExec_ns(this);
186 _programExec = new ProgramExec_ns(this);
187
188 _walker = new PathWalker_NS(this);
189
190 _sarcophagusDeltaX = 0;
191 _movingSarcophagus = false;
192 _freeSarcophagusSlotX = INITIAL_FREE_SARCOPHAGUS_SLOT_X;
193
194 num_foglie = 0;
195
196 _intro = false;
197 _inTestResult = false;
198
199 _location._animations.push_front(_char._ani);
200
201 _saveLoad = new SaveLoad_ns(this, _saveFileMan);
202
203 initInventory();
204 setupBalloonManager();
205
206 _score = 1;
207
208 _testResultLabels[0] = 0;
209 _testResultLabels[1] = 0;
210
211 Parallaction::init();
212
213 return Common::kNoError;
214 }
215
~Parallaction_ns()216 Parallaction_ns::~Parallaction_ns() {
217 freeFonts();
218
219 // TODO: we may want to add a ~Character instead
220 freeCharacter();
221 _char._ani.reset();
222
223 destroyInventory();
224
225 delete _locationParser;
226 delete _programParser;
227 freeLocation(true);
228
229 _location._animations.remove(_char._ani);
230
231 delete _walker;
232
233 destroyTestResultLabels();
234 }
235
destroyTestResultLabels()236 void Parallaction_ns::destroyTestResultLabels() {
237 for (int i = 0; i < 2; ++i) {
238 _gfx->unregisterLabel(_testResultLabels[i]);
239 delete _testResultLabels[i];
240 _testResultLabels[i] = 0;
241 }
242 }
243
freeFonts()244 void Parallaction_ns::freeFonts() {
245
246 delete _dialogueFont;
247 delete _labelFont;
248 delete _menuFont;
249 delete _introFont;
250
251 _menuFont = 0;
252 _dialogueFont = 0;
253 _labelFont = 0;
254 _introFont = 0;
255 }
256
257
callFunction(uint index,void * parm)258 void Parallaction_ns::callFunction(uint index, void* parm) {
259 assert(index < 25); // magic value 25 is maximum # of callables for Nippon Safes
260
261 (this->*_callables[index])(parm);
262 }
263
processGameEvent(int event)264 bool Parallaction_ns::processGameEvent(int event) {
265 if (event == kEvNone) {
266 return true;
267 }
268
269 bool c = true;
270 _input->stopHovering();
271
272 switch (event) {
273 case kEvSaveGame:
274 _saveLoad->saveGame();
275 break;
276
277 case kEvLoadGame:
278 _saveLoad->loadGame();
279 break;
280 }
281
282 _input->setArrowCursor();
283 _input->setMouseState(MOUSE_ENABLED_SHOW);
284
285 return c;
286 }
287
go()288 Common::Error Parallaction_ns::go() {
289 _saveLoad->renameOldSavefiles();
290
291 _globalFlagsNames = _disk->loadTable("global");
292
293 startGui();
294
295 while (!shouldQuit()) {
296 runGame();
297 }
298
299 return Common::kNoError;
300 }
301
changeBackground(const char * background,const char * mask,const char * path)302 void Parallaction_ns::changeBackground(const char* background, const char* mask, const char* path) {
303 Palette pal;
304
305 uint16 v2 = 0;
306 if (!scumm_stricmp(background, "final")) {
307 _gfx->clearScreen();
308 for (uint16 _si = 0; _si < 32; _si++) {
309 pal.setEntry(_si, v2, v2, v2);
310 v2 += 4;
311 }
312
313 _system->delayMillis(20);
314 _gfx->setPalette(pal);
315 _gfx->updateScreen();
316 return;
317 }
318
319 if (path == 0) {
320 path = mask;
321 }
322
323 BackgroundInfo *info = new BackgroundInfo;
324 _disk->loadScenery(*info, background, mask, path);
325 _gfx->setBackground(kBackgroundLocation, info);
326 }
327
328
runPendingZones()329 void Parallaction_ns::runPendingZones() {
330 if (_activeZone) {
331 ZonePtr z = _activeZone; // speak Zone or sound
332 _activeZone.reset();
333 runZone(z);
334 }
335 }
336
337 // changeLocation handles transitions between locations, and is able to display slides
338 // between one and the other.
339 //
changeLocation()340 void Parallaction_ns::changeLocation() {
341 if (_newLocationName.empty()) {
342 return;
343 }
344
345 char location[200];
346 Common::strlcpy(location, _newLocationName.c_str(), 200);
347 Common::strlcpy(_location._name, _newLocationName.c_str(), 100);
348
349 debugC(1, kDebugExec, "changeLocation(%s)", location);
350
351 MouseTriState oldMouseState = _input->getMouseState();
352 _input->setMouseState(MOUSE_DISABLED);
353
354 if (!_intro) {
355 // prevent music changes during the introduction
356 _soundManI->playLocationMusic(location);
357 }
358
359 _input->stopHovering();
360 // this is still needed to remove the floatingLabel
361 _gfx->freeLabels();
362
363 _zoneTrap.reset();
364
365 _input->setArrowCursor();
366
367 _gfx->showGfxObj(_char._ani->gfxobj, false);
368
369 LocationName locname;
370 locname.bind(location);
371
372 freeLocation(false);
373
374 if (locname.hasSlide()) {
375 showSlide(locname.slide());
376 GfxObj *label = _gfx->createLabel(_menuFont, _location._slideText[0].c_str(), 1);
377 _gfx->showLabel(label, CENTER_LABEL_HORIZONTAL, 14);
378 _gfx->updateScreen();
379
380 _input->waitForButtonEvent(kMouseLeftUp);
381 _gfx->unregisterLabel(label);
382 delete label;
383 }
384
385 if (locname.hasCharacter()) {
386 changeCharacter(locname.character());
387 }
388
389 Common::strlcpy(g_saveData1, locname.location(), 30);
390 parseLocation(g_saveData1);
391
392 if (_location._startPosition.x != -1000) {
393 _char._ani->setX(_location._startPosition.x);
394 _char._ani->setY(_location._startPosition.y);
395 _char._ani->setF(_location._startFrame);
396 _location._startPosition.y = -1000;
397 _location._startPosition.x = -1000;
398 }
399
400
401 _gfx->setBlackPalette();
402 _gfx->updateScreen();
403
404 // BUG #1837503: kEngineChangeLocation flag must be cleared *before* commands
405 // and acommands are executed, so that it can be set again if needed.
406 g_engineFlags &= ~kEngineChangeLocation;
407
408 _cmdExec->run(_location._commands);
409
410 doLocationEnterTransition();
411
412 _cmdExec->run(_location._aCommands);
413
414 if (_location._hasSound)
415 _soundManI->playSfx(_location._soundFile, 0, true);
416
417 if (!_intro) {
418 _input->setMouseState(oldMouseState);
419 // WORKAROUND: Fix a script bug in the Multilingual DOS version of
420 // Nippon Safes: the mouse cursor is incorrectly hidden outside the
421 // cave at the end of the game. Fix it here.
422 if (!strcmp(_location._name, "ingressocav"))
423 _input->setMouseState(MOUSE_ENABLED_SHOW);
424 }
425
426 debugC(1, kDebugExec, "changeLocation() done");
427 _newLocationName.clear();
428 }
429
430
parseLocation(const char * filename)431 void Parallaction_ns::parseLocation(const char *filename) {
432 debugC(1, kDebugParser, "parseLocation('%s')", filename);
433
434 allocateLocationSlot(filename);
435 Script *script = _disk->loadLocation(filename);
436
437 // TODO: the following two lines are specific to Nippon Safes
438 // and should be moved into something like 'initializeParsing()'
439 _location._hasSound = false;
440
441 _locationParser->parse(script);
442
443 delete script;
444
445 // this loads animation scripts
446 AnimationList::iterator it = _location._animations.begin();
447 for ( ; it != _location._animations.end(); ++it) {
448 if (!(*it)->_scriptName.empty()) {
449 loadProgram(*it, (*it)->_scriptName.c_str());
450 }
451 }
452
453 debugC(1, kDebugParser, "parseLocation('%s') done", filename);
454 return;
455 }
456
457
458
changeCharacter(const char * name)459 void Parallaction_ns::changeCharacter(const char *name) {
460 debugC(1, kDebugExec, "changeCharacter(%s)", name);
461
462 _char.setName(name);
463
464 if (!scumm_stricmp(_char.getFullName(), _characterName1)) {
465 debugC(3, kDebugExec, "changeCharacter: nothing done");
466 return;
467 }
468
469 freeCharacter();
470
471 _char._ani->gfxobj = _gfx->loadCharacterAnim(_char.getFullName());
472
473 if (!_char.dummy()) {
474 _char._head = _disk->loadHead(_char.getBaseName());
475 _char._talk = _disk->loadTalk(_char.getBaseName());
476 _objects = _disk->loadObjects(_char.getBaseName());
477 _objectsNames = _disk->loadTable(_char.getBaseName());
478
479 if (!_intro) {
480 // prevent music changes during the introduction
481 _soundManI->playCharacterMusic(_char.getBaseName());
482 }
483
484 // The original engine used to reload 'common' only on loadgames. We are reloading here since 'common'
485 // contains character specific stuff. This causes crashes like bug #1816899, because parseLocation tries
486 // to reload scripts but the data archive selected is occasionally wrong. This has been solved by having
487 // parseLocation only load scripts when they aren't already loaded - which it should have done since the
488 // beginning nevertheless.
489 if (!(getFeatures() & GF_DEMO))
490 parseLocation("common");
491 }
492
493 strcpy(_characterName1, _char.getFullName());
494
495 debugC(3, kDebugExec, "changeCharacter: switch completed");
496
497 return;
498 }
499
freeCharacter()500 void Parallaction_ns::freeCharacter() {
501 _gfx->freeCharacterObjects();
502
503 delete _char._talk;
504 delete _char._head;
505 delete _char._ani->gfxobj;
506 delete _objects;
507 delete _objectsNames;
508
509 _char._talk = 0;
510 _char._head = 0;
511 _char._ani->gfxobj = 0;
512
513 _objects = 0;
514 _objectsNames = 0;
515 }
516
freeLocation(bool removeAll)517 void Parallaction_ns::freeLocation(bool removeAll) {
518 debugC(2, kDebugExec, "freeLocation");
519
520 _soundManI->stopSfx(0);
521 _soundManI->stopSfx(1);
522 _soundManI->stopSfx(2);
523 _soundManI->stopSfx(3);
524
525 _localFlagNames->clear();
526
527 _gfx->freeLocationObjects();
528
529 _location._animations.remove(_char._ani);
530 _location.cleanup(removeAll);
531 _location._animations.push_front(_char._ani);
532 }
533
cleanupGame()534 void Parallaction_ns::cleanupGame() {
535 _soundManI->stopMusic();
536
537 _inTestResult = false;
538 g_engineFlags &= ~kEngineTransformedDonna;
539
540 _numLocations = 0;
541 g_globalFlags = 0;
542 memset(_localFlags, 0, sizeof(_localFlags));
543 memset(_locationNames, 0, sizeof(_locationNames));
544
545 _location.freeZones(true);
546
547 _score = 0;
548 _freeSarcophagusSlotX = INITIAL_FREE_SARCOPHAGUS_SLOT_X;
549 _movingSarcophagus = false;
550 }
551
updateWalkers()552 void Parallaction_ns::updateWalkers() {
553 _walker->walk();
554 }
555
556
scheduleWalk(int16 x,int16 y,bool fromUser)557 void Parallaction_ns::scheduleWalk(int16 x, int16 y, bool fromUser) {
558 AnimationPtr a = _char._ani;
559
560 if ((a->_flags & kFlagsRemove) || (a->_flags & kFlagsActive) == 0) {
561 return;
562 }
563
564 _walker->buildPath(a, x, y);
565 g_engineFlags |= kEngineWalking;
566 }
567
568 }// namespace Parallaction
569