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