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/util.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 namespace Parallaction {
36
37
38 const char *Parallaction_br::_partNames[] = {
39 "PART0",
40 "PART1",
41 "PART2",
42 "PART3",
43 "PART4"
44 };
45
Parallaction_br(OSystem * syst,const PARALLACTIONGameDescription * gameDesc)46 Parallaction_br::Parallaction_br(OSystem* syst, const PARALLACTIONGameDescription *gameDesc) : Parallaction(syst, gameDesc),
47 _locationParser(0), _programParser(0), _soundManI(0) {
48 _audioCommandsNamesRes = 0;
49 _part = 0;
50 _nextPart = 0;
51 _subtitleY = 0;
52 _subtitle[0] = 0;
53 _subtitle[1] = 0;
54 _charInventories[0] = 0;
55 _charInventories[1] = 0;
56 _charInventories[2] = 0;
57 _countersNames = 0;
58 _callables = 0;
59 _walker = 0;
60 }
61
init()62 Common::Error Parallaction_br::init() {
63
64 _screenWidth = 640;
65 _screenHeight = 400;
66
67 if (getPlatform() == Common::kPlatformDOS) {
68 if (getFeatures() & GF_DEMO) {
69 _disk = new DosDemoDisk_br(this);
70 } else {
71 _disk = new DosDisk_br(this);
72 }
73 _disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters.
74 _soundManI = new DosSoundMan_br(this);
75 } else {
76 _disk = new AmigaDisk_br(this);
77 _disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters.
78 _soundManI = new AmigaSoundMan_br(this);
79 }
80
81 _disk->init();
82 _soundMan = new SoundMan(_soundManI);
83
84 initResources();
85 initFonts();
86 _locationParser = new LocationParser_br(this);
87 _locationParser->init();
88 _programParser = new ProgramParser_br(this);
89 _programParser->init();
90
91 _cmdExec = new CommandExec_br(this);
92 _programExec = new ProgramExec_br(this);
93
94 _walker = new PathWalker_BR(this);
95
96 _part = -1;
97 _nextPart = -1;
98
99 _subtitle[0] = 0;
100 _subtitle[1] = 0;
101
102 memset(_zoneFlags, 0, sizeof(_zoneFlags));
103
104 _countersNames = 0;
105
106 _saveLoad = new SaveLoad_br(this, _saveFileMan);
107
108 initInventory();
109 setupBalloonManager();
110
111 Parallaction::init();
112
113 return Common::kNoError;
114 }
115
~Parallaction_br()116 Parallaction_br::~Parallaction_br() {
117 freeFonts();
118 freeCharacter();
119
120 destroyInventory();
121
122 delete _objects;
123
124 delete _locationParser;
125 delete _programParser;
126
127 _location._animations.remove(_char._ani);
128
129 delete _walker;
130 }
131
callFunction(uint index,void * parm)132 void Parallaction_br::callFunction(uint index, void* parm) {
133 assert(index < 6); // magic value 6 is maximum # of callables for Big Red Adventure
134
135 (this->*_callables[index])(parm);
136 }
137
processGameEvent(int event)138 bool Parallaction_br::processGameEvent(int event) {
139 if (event == kEvNone) {
140 return true;
141 }
142
143 bool c = true;
144 _input->stopHovering();
145
146 switch (event) {
147 case kEvIngameMenu:
148 startIngameMenu();
149 c = false;
150 break;
151 }
152
153 _input->setArrowCursor();
154
155 return c;
156 }
157
go()158 Common::Error Parallaction_br::go() {
159
160 bool splash = true;
161
162 while (!shouldQuit()) {
163
164 if (getFeatures() & GF_DEMO) {
165 scheduleLocationSwitch("camalb");
166 _nextPart = 1;
167 _input->_inputMode = Input::kInputModeGame;
168 } else {
169 startGui(splash);
170 // don't show splash after first time
171 splash = false;
172 }
173
174 // initCharacter();
175
176 while (((g_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) {
177 runGame();
178 }
179 g_engineFlags &= ~kEngineReturn;
180
181 cleanupGame();
182 }
183
184 return Common::kNoError;
185 }
186
187
freeFonts()188 void Parallaction_br::freeFonts() {
189 delete _menuFont;
190 _menuFont = 0;
191
192 delete _dialogueFont;
193 _dialogueFont = 0;
194
195 // no need to delete _labelFont, since it is using the same buffer as _menuFont
196 _labelFont = 0;
197 }
198
199
runPendingZones()200 void Parallaction_br::runPendingZones() {
201 ZonePtr z;
202
203 _cmdExec->runSuspended();
204
205 if (_activeZone) {
206 z = _activeZone; // speak Zone or sound
207 _activeZone.reset();
208 if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) {
209 enterDialogueMode(z);
210 } else {
211 runZone(z); // FIXME: BRA doesn't handle sound yet
212 }
213 }
214
215 if (_activeZone2) {
216 z = _activeZone2; // speak Zone or sound
217 _activeZone2.reset();
218 if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) {
219 enterDialogueMode(z);
220 } else {
221 runZone(z); // FIXME: BRA doesn't handle sound yet
222 }
223 }
224 }
225
freeCharacter()226 void Parallaction_br::freeCharacter() {
227 _gfx->freeCharacterObjects();
228
229 delete _char._talk;
230 delete _char._ani->gfxobj;
231
232 _char._talk = 0;
233 _char._ani->gfxobj = 0;
234 }
235
freeLocation(bool removeAll)236 void Parallaction_br::freeLocation(bool removeAll) {
237 // free open location stuff
238 clearSubtitles();
239
240 _localFlagNames->clear();
241
242 _gfx->freeLocationObjects();
243
244 // save zone and animation flags
245 ZoneList::iterator zit = _location._zones.begin();
246 for ( ; zit != _location._zones.end(); ++zit) {
247 restoreOrSaveZoneFlags(*zit, false);
248 }
249 AnimationList::iterator ait = _location._animations.begin();
250 for ( ; ait != _location._animations.end(); ++ait) {
251 restoreOrSaveZoneFlags(*ait, false);
252 }
253
254 _location._animations.remove(_char._ani);
255 _location.cleanup(removeAll);
256 _location._animations.push_front(_char._ani);
257
258 }
259
cleanupGame()260 void Parallaction_br::cleanupGame() {
261 freeLocation(true);
262
263 freeCharacter();
264
265 delete _globalFlagsNames;
266 delete _objectsNames;
267 delete _countersNames;
268
269 _globalFlagsNames = 0;
270 _objectsNames = 0;
271 _countersNames = 0;
272
273 _numLocations = 0;
274 g_globalFlags = 0;
275 memset(_localFlags, 0, sizeof(_localFlags));
276 memset(_locationNames, 0, sizeof(_locationNames));
277 memset(_zoneFlags, 0, sizeof(_zoneFlags));
278 }
279
280
changeLocation()281 void Parallaction_br::changeLocation() {
282 if (_newLocationName.empty()) {
283 return;
284 }
285
286 if (_nextPart != -1) {
287 cleanupGame();
288
289 // more cleanup needed for part changes (see also saveload)
290 g_globalFlags = 0;
291 cleanInventory(true);
292 strcpy(_characterName1, "null");
293
294 _part = _nextPart;
295
296 if (getFeatures() & GF_DEMO) {
297 assert(_part == 1);
298 } else {
299 assert(_part >= 0 && _part <= 4);
300 }
301
302 _disk->selectArchive(_partNames[_part]);
303
304 memset(_counters, 0, sizeof(_counters));
305
306 _globalFlagsNames = _disk->loadTable("global");
307 _objectsNames = _disk->loadTable("objects");
308 _countersNames = _disk->loadTable("counters");
309
310 // TODO: maybe handle this into Disk
311 delete _objects;
312 if (getPlatform() == Common::kPlatformDOS) {
313 _objects = _disk->loadObjects("icone.ico");
314 } else {
315 _objects = _disk->loadObjects("icons.ico", _part);
316 }
317
318 parseLocation("common.slf");
319 }
320
321 freeLocation(false);
322 // load new location
323 Common::strlcpy(_location._name, _newLocationName.c_str(), 100);
324 parseLocation(_location._name);
325
326 if (_location._startPosition.x != -1000) {
327 _char._ani->setFoot(_location._startPosition);
328 _char._ani->setF(_location._startFrame);
329 }
330
331 // re-link the follower animation
332 setFollower(_followerName);
333 if (_follower) {
334 Common::Point p = _location._followerStartPosition;
335 if (p.x == -1000) {
336 _char._ani->getFoot(p);
337 }
338 _follower->setFoot(p);
339 _follower->setF(_location._followerStartFrame);
340 }
341
342 _location._startPosition.x = -1000;
343 _location._startPosition.y = -1000;
344 _location._followerStartPosition.x = -1000;
345 _location._followerStartPosition.y = -1000;
346
347 _gfx->setScrollPosX(0);
348 _gfx->setScrollPosY(0);
349 if (_char._ani->gfxobj) {
350 Common::Point foot;
351 _char._ani->getFoot(foot);
352
353 if (foot.x > 550)
354 _gfx->setScrollPosX(320);
355
356 if (foot.y > 350)
357 _gfx->setScrollPosY(foot.y - 350);
358 }
359
360 // kFlagsRemove is cleared because the character is visible by default.
361 // Commands can hide the character, anyway.
362 _char._ani->_flags &= ~kFlagsRemove;
363 _cmdExec->run(_location._commands);
364
365 doLocationEnterTransition();
366
367 _cmdExec->run(_location._aCommands);
368
369 // NOTE: music should not started here!
370 // TODO: implement the music commands which control music execution
371 _soundMan->execute(SC_PLAYMUSIC);
372
373 g_engineFlags &= ~kEngineChangeLocation;
374 _newLocationName.clear();
375 _nextPart = -1;
376 }
377
378 // FIXME: Parallaction_br::parseLocation() is now a verbatim copy of the same routine from Parallaction_ns.
parseLocation(const char * filename)379 void Parallaction_br::parseLocation(const char *filename) {
380 debugC(1, kDebugParser, "parseLocation('%s')", filename);
381
382 // find a new available slot
383 allocateLocationSlot(filename);
384 Script *script = _disk->loadLocation(filename);
385
386 // parse the text file
387 LocationParserOutput_br out;
388 _locationParser->parse(script, &out);
389 assert(out._info);
390 delete script;
391
392 bool visited = getLocationFlags() & kFlagsVisited;
393
394 // load background, mask and path
395 _disk->loadScenery(*out._info,
396 out._backgroundName.empty() ? 0 : out._backgroundName.c_str(),
397 out._maskName.empty() ? 0 : out._maskName.c_str(),
398 out._pathName.empty() ? 0 : out._pathName.c_str());
399 // assign background
400 _gfx->setBackground(kBackgroundLocation, out._info);
401
402
403 // process zones
404 ZoneList::iterator zit = _location._zones.begin();
405 for ( ; zit != _location._zones.end(); ++zit) {
406 ZonePtr z = *zit;
407 // restore the flags if the location has already been visited
408 restoreOrSaveZoneFlags(z, visited);
409
410 // (re)link the bounding animation if needed
411 if (z->_flags & kFlagsAnimLinked) {
412 z->_linkedAnim = _location.findAnimation(z->_linkedName.c_str());
413 }
414
415 bool visible = (z->_flags & kFlagsRemove) == 0;
416 if (visible) {
417 showZone(z, visible);
418 }
419 }
420
421 // load the character (must be done before animations are processed)
422 if (!out._characterName.empty()) {
423 changeCharacter(out._characterName.c_str());
424 }
425
426 // process animations
427 AnimationList::iterator ait = _location._animations.begin();
428 for ( ; ait != _location._animations.end(); ++ait) {
429 // restore the flags if the location has already been visited
430 restoreOrSaveZoneFlags(*ait, visited);
431
432 // load the script
433 if (!(*ait)->_scriptName.empty()) {
434 loadProgram(*ait, (*ait)->_scriptName.c_str());
435 }
436 }
437
438 debugC(1, kDebugParser, "parseLocation('%s') done", filename);
439 return;
440 }
441
loadProgram(AnimationPtr a,const char * filename)442 void Parallaction_br::loadProgram(AnimationPtr a, const char *filename) {
443 debugC(1, kDebugParser, "loadProgram(Animation: %s, script: %s)", a->_name, filename);
444
445 Script *script = _disk->loadScript(filename);
446 ProgramPtr program(new Program);
447 program->_anim = a;
448
449 _programParser->parse(script, program);
450
451 delete script;
452
453 _location._programs.push_back(program);
454
455 debugC(1, kDebugParser, "loadProgram() done");
456
457 return;
458 }
459
460
461
changeCharacter(const char * name)462 void Parallaction_br::changeCharacter(const char *name) {
463
464 const char *charName = _char.getName();
465
466 if (scumm_stricmp(charName, name)) {
467 freeCharacter();
468
469 debugC(1, kDebugExec, "changeCharacter(%s)", name);
470
471 _char.setName(name);
472 _char._ani->gfxobj = _gfx->loadCharacterAnim(name);
473 _char._talk = _disk->loadTalk(name);
474
475 /* TODO: adjust inventories as following
476 * 1) if not on game load, then copy _inventory to the right slot of _charInventories
477 * 2) copy the new inventory from the right slot of _charInventories
478 */
479 }
480
481 _char._ani->_flags |= kFlagsActive;
482 }
483
counterExists(const Common::String & name)484 bool Parallaction_br::counterExists(const Common::String &name) {
485 return Table::notFound != _countersNames->lookup(name.c_str());
486 }
487
getCounterValue(const Common::String & name)488 int Parallaction_br::getCounterValue(const Common::String &name) {
489 int index = _countersNames->lookup(name.c_str());
490 if (index != Table::notFound) {
491 return _counters[index - 1];
492 }
493 return 0;
494 }
495
setCounterValue(const Common::String & name,int value)496 void Parallaction_br::setCounterValue(const Common::String &name, int value) {
497 int index = _countersNames->lookup(name.c_str());
498 if (index != Table::notFound) {
499 _counters[index - 1] = value;
500 }
501 }
502
testCounterCondition(const Common::String & name,int op,int value)503 void Parallaction_br::testCounterCondition(const Common::String &name, int op, int value) {
504 int index = _countersNames->lookup(name.c_str());
505 if (index == Table::notFound) {
506 clearLocationFlags(kFlagsTestTrue);
507 return;
508 }
509
510 int c = _counters[index - 1];
511
512 // these definitions must match those in parser_br.cpp
513 #define CMD_TEST 25
514 #define CMD_TEST_GT 26
515 #define CMD_TEST_LT 27
516
517 bool res = false;
518 switch (op) {
519 case CMD_TEST:
520 res = (c == value);
521 break;
522
523 case CMD_TEST_GT:
524 res = (c > value);
525 break;
526
527 case CMD_TEST_LT:
528 res = (c < value);
529 break;
530
531 default:
532 error("unknown operator in testCounterCondition");
533 }
534
535 if (res) {
536 setLocationFlags(kFlagsTestTrue);
537 } else {
538 clearLocationFlags(kFlagsTestTrue);
539 }
540 }
541
updateWalkers()542 void Parallaction_br::updateWalkers() {
543 _walker->walk();
544 }
545
scheduleWalk(int16 x,int16 y,bool fromUser)546 void Parallaction_br::scheduleWalk(int16 x, int16 y, bool fromUser) {
547 AnimationPtr a = _char._ani;
548
549 if ((a->_flags & kFlagsRemove) || (a->_flags & kFlagsActive) == 0) {
550 return;
551 }
552
553 _walker->setCharacterPath(a, x, y);
554
555 if (!fromUser) {
556 _walker->stopFollower();
557 } else {
558 if (_follower) {
559 _walker->setFollowerPath(_follower, x, y);
560 }
561 }
562
563 g_engineFlags |= kEngineWalking;
564 }
565
setFollower(const Common::String & name)566 void Parallaction_br::setFollower(const Common::String &name) {
567 if (name.empty()) {
568 _followerName.clear();
569 _follower.reset();
570 } else {
571 _followerName = name;
572 _follower = _location.findAnimation(name.c_str());
573 }
574 }
575
restoreOrSaveZoneFlags(ZonePtr z,bool restore)576 void Parallaction_br::restoreOrSaveZoneFlags(ZonePtr z, bool restore) {
577 if ((z->_locationIndex == INVALID_LOCATION_INDEX) || (z->_index == INVALID_ZONE_INDEX)) {
578 return;
579 }
580
581 if (restore) {
582 z->_flags = _zoneFlags[z->_locationIndex][z->_index];
583 } else {
584 _zoneFlags[z->_locationIndex][z->_index] = z->_flags;
585 }
586 }
587
getSfxStatus()588 int Parallaction_br::getSfxStatus() {
589 if (!_soundManI) {
590 return -1;
591 }
592 return _soundManI->isSfxEnabled() ? 1 : 0;
593 }
594
getMusicStatus()595 int Parallaction_br::getMusicStatus() {
596 if (!_soundManI) {
597 return -1;
598 }
599 return _soundManI->isMusicEnabled() ? 1 : 0;
600 }
601
enableSfx(bool enable)602 void Parallaction_br::enableSfx(bool enable) {
603 if (_soundManI) {
604 _soundManI->enableSfx(enable);
605 }
606 }
607
enableMusic(bool enable)608 void Parallaction_br::enableMusic(bool enable) {
609 if (_soundManI) {
610 _soundManI->enableMusic(enable);
611 }
612 }
613
614 } // namespace Parallaction
615